Skip to main content

Self-Host Nextcloud on Docker 2026

·OSSAlt Team
nextclouddockerself-hostingcloud-storagegoogle-drivetraefiksslbackup2026
Share:

Self-Host Nextcloud on Docker 2026

TL;DR

Nextcloud is the most complete self-hosted cloud storage platform available in 2026. A single Docker Compose stack — Nextcloud, PostgreSQL, Redis, and Traefik — runs comfortably on a $6/month VPS with 1GB RAM and replaces Google Drive, Google Calendar, Google Contacts, and basic document collaboration for up to 5–10 users. You own the data, control the encryption, and pay $72/year instead of $120+/year for Google One. This guide covers both Nextcloud AIO (All-in-One) and the manual postgres+redis stack, SSL termination with Traefik, and a production-grade backup strategy using rclone.

Key Takeaways

  • Minimum specs: 1GB RAM, 1 vCPU, 20GB storage — a $6/month Hetzner or DigitalOcean VPS handles 5 users comfortably
  • Two deployment paths: Nextcloud AIO (single container, automated SSL, batteries-included) vs manual stack (more control, easier to debug, better for existing infrastructure)
  • SSL automation: Traefik with Let's Encrypt handles cert provisioning and renewal with zero manual certificate management
  • Data replacement: Files, Calendar (CalDAV), Contacts (CardDAV), Notes, Bookmarks, and Talk (video chat) — Google Workspace feature parity for personal and small team use
  • Backup minimum: Nextcloud requires three-component backups — database (PostgreSQL dump), file data (volume snapshot), and Nextcloud config — skipping any one leaves you unable to restore
  • Offsite backup: rclone + a $1–2/month object storage bucket (Backblaze B2, Wasabi) makes backups genuinely disaster-proof

Why Self-Host Nextcloud in 2026

Google Drive price increases, Google One storage bundling, and heightened awareness of cloud data retention policies have pushed more teams and individuals toward self-hosted alternatives. Nextcloud's case has also strengthened: version 29 (released 2024) improved performance significantly, the Nextcloud Office integration (Collabora Online) matured, and the ecosystem of apps — including AI assistant integrations — expanded.

The practical argument for self-hosting in 2026:

Cost: Google One's 2TB plan costs $99.99/year. A Hetzner CX22 (2 vCPU, 4GB RAM, 40GB SSD) costs €5.77/month — about $75/year — with the option to attach cheap additional volumes for $0.052/GB/month. At 2TB of actual storage on Hetzner volumes, total cost is roughly $125/year, comparable to Google One but with no per-user pricing and full data sovereignty.

Data ownership: Nextcloud data stays on your server, in your jurisdiction, encrypted at rest if you configure it. Google scans files for policy compliance and may suspend accounts for content violations. There is no equivalent risk with a self-hosted instance.

Feature scope: Nextcloud Hub 9 covers Files (with desktop/mobile sync clients), Calendar (CalDAV), Contacts (CardDAV), Mail client, Notes, Talk (video and audio calls), Forms, Deck (Kanban), and Nextcloud Office (Collabora Online for document editing). For small teams, this eliminates multiple SaaS subscriptions.

Compliance: For GDPR, HIPAA, or SOC 2-adjacent requirements where data must stay within specific geographic boundaries, self-hosted Nextcloud is a straightforward answer. Cloud storage providers require complex DPAs and rely on mechanisms that regulatory bodies periodically challenge.


Choosing a Deployment Path: AIO vs Manual Stack

Nextcloud offers two distinct Docker deployment strategies with meaningfully different tradeoffs.

Nextcloud AIO (All-in-One)

Nextcloud AIO is a master container that manages the entire stack: Nextcloud itself, PostgreSQL, Redis, Nextcloud Office (Collabora), Clamav (virus scanning), Imaginary (media processing), Elasticsearch (optional full-text search), and backup. It exposes an admin interface on port 8080 for configuration.

AIO Pros:

  • Single-command deployment
  • Automated SSL via Caddy (built-in)
  • Automated daily backups via built-in backup container
  • All components versioned and tested together
  • Nextcloud-recommended path for most users

AIO Cons:

  • Less transparent — harder to debug individual components
  • Requires port 443 to be available on the host (conflicts with existing Nginx/Traefik setups)
  • Less flexible for custom reverse proxy configurations
  • AIO's Caddy handles SSL, which may conflict with existing certificate management

AIO Quick Start:

docker run \
  --sig-proxy=false \
  --name nextcloud-aio-mastercontainer \
  --restart always \
  --publish 80:80 \
  --publish 8080:8080 \
  --publish 8443:8443 \
  --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
  --volume /var/run/docker.sock:/var/run/docker.sock:ro \
  nextcloud/all-in-one:latest

Access https://your-server-ip:8080 to complete setup through the web UI. AIO handles the rest.

Manual Stack (PostgreSQL + Redis + Nginx/Traefik)

The manual stack gives you full control over each component and integrates cleanly with existing infrastructure. This is the right choice if you already run Traefik as a reverse proxy for other services, need custom Nginx configuration, or want to understand exactly what's running.

This guide uses the manual stack for the full setup walkthrough.


Manual Stack: Docker Compose Setup

Prerequisites

  • A VPS with 1GB+ RAM and Ubuntu 22.04 or 24.04
  • Docker and Docker Compose v2 installed
  • A domain pointing to your server's IP (e.g., cloud.yourdomain.com)
  • Ports 80 and 443 open in your firewall

Directory Structure

mkdir -p /opt/nextcloud/{data,config}
cd /opt/nextcloud

docker-compose.yml

services:
  traefik:
    image: traefik:v3.0
    restart: always
    command:
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=admin@yourdomain.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-letsencrypt:/letsencrypt
    networks:
      - proxy

  db:
    image: postgres:16-alpine
    restart: always
    environment:
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - internal

  redis:
    image: redis:7-alpine
    restart: always
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis-data:/data
    networks:
      - internal

  nextcloud:
    image: nextcloud:29-apache
    restart: always
    depends_on:
      - db
      - redis
    environment:
      POSTGRES_HOST: db
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      NEXTCLOUD_ADMIN_USER: admin
      NEXTCLOUD_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      NEXTCLOUD_TRUSTED_DOMAINS: cloud.yourdomain.com
      REDIS_HOST: redis
      REDIS_HOST_PASSWORD: ${REDIS_PASSWORD}
      APACHE_DISABLE_REWRITE_IP: 1
      TRUSTED_PROXIES: traefik
      OVERWRITEPROTOCOL: https
      OVERWRITECLIURL: https://cloud.yourdomain.com
    volumes:
      - nextcloud-data:/var/www/html
      - ./config:/var/www/html/config
    networks:
      - proxy
      - internal
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nextcloud.rule=Host(`cloud.yourdomain.com`)"
      - "traefik.http.routers.nextcloud.entrypoints=websecure"
      - "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
      - "traefik.http.middlewares.nextcloud-headers.headers.customResponseHeaders.X-Frame-Options=SAMEORIGIN"
      - "traefik.http.middlewares.nextcloud-headers.headers.customResponseHeaders.X-Content-Type-Options=nosniff"
      - "traefik.http.middlewares.nextcloud-caldav.redirectregex.regex=^https://(.*)/.well-known/(card|cal)dav"
      - "traefik.http.middlewares.nextcloud-caldav.redirectregex.replacement=https://$${1}/remote.php/dav/"
      - "traefik.http.middlewares.nextcloud-caldav.redirectregex.permanent=true"
      - "traefik.http.routers.nextcloud.middlewares=nextcloud-headers,nextcloud-caldav"

  nextcloud-cron:
    image: nextcloud:29-apache
    restart: always
    depends_on:
      - db
      - redis
    volumes:
      - nextcloud-data:/var/www/html
      - ./config:/var/www/html/config
    entrypoint: /cron.sh
    networks:
      - internal

volumes:
  db-data:
  redis-data:
  nextcloud-data:
  traefik-letsencrypt:

networks:
  proxy:
    external: false
  internal:
    external: false

Environment File

# /opt/nextcloud/.env
DB_PASSWORD=change_this_strong_password_32chars
REDIS_PASSWORD=change_this_redis_password_16chars
ADMIN_PASSWORD=change_this_admin_password

Generate strong passwords:

openssl rand -base64 32 | tr -d '/+=' | head -c 32

Start the Stack

cd /opt/nextcloud
docker compose up -d

# Watch Nextcloud initialize (takes 30–60 seconds on first boot)
docker compose logs -f nextcloud

Once initialization completes, access https://cloud.yourdomain.com. Traefik handles Let's Encrypt certificate provisioning automatically on first request.


SSL with Traefik: How It Works

Traefik's ACME integration handles certificate provisioning through the ACME TLS challenge:

  1. Traefik receives the first HTTPS request for cloud.yourdomain.com
  2. It contacts Let's Encrypt and proves domain control via a TLS-ALPN-01 challenge (no HTTP port 80 required)
  3. Let's Encrypt issues a 90-day certificate
  4. Traefik stores it in /letsencrypt/acme.json and automatically renews at 30 days before expiry

The acme.json file is stored in the traefik-letsencrypt Docker volume — back this up alongside your other volumes.

Caddy as an Alternative

Caddy is simpler to configure for single-service setups:

# Caddyfile
cloud.yourdomain.com {
    reverse_proxy nextcloud:80

    header {
        X-Content-Type-Options nosniff
        X-Frame-Options SAMEORIGIN
    }

    redir /.well-known/carddav /remote.php/dav/ permanent
    redir /.well-known/caldav /remote.php/dav/ permanent
}

Caddy's advantage is zero-configuration HTTPS — it reads the Caddyfile and handles all certificate management. Traefik's advantage is centralized routing for multi-service environments, which is preferable if you're running multiple Docker services behind one reverse proxy.


Post-Installation Configuration

Optimize Nextcloud for Performance

Edit /opt/nextcloud/config/config.php after the first run:

<?php
$CONFIG = array(
  // Memory caching with APCu (local) + Redis (distributed)
  'memcache.local' => '\\OC\\Memcache\\APCu',
  'memcache.distributed' => '\\OC\\Memcache\\Redis',
  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'redis' => array(
    'host' => 'redis',
    'port' => 6379,
    'password' => 'your_redis_password',
    'timeout' => 1.5,
  ),

  // Set default phone region
  'default_phone_region' => 'US',

  // Maintenance window (off-peak updates)
  'maintenance_window_start' => 1,

  // Chunked upload size (4MB for slower connections)
  'upload_max_filesize' => '16G',
  'max_upload_size' => '16G',
);

Increase PHP Upload Limits

The default Nextcloud Apache image has conservative upload limits. Override via environment or a custom .htaccess:

# Set via occ command inside the container
docker compose exec -u www-data nextcloud php occ config:system:set upload_max_filesize --value="16G"
docker compose exec -u www-data nextcloud php occ config:system:set post_max_size --value="16G"

Run Maintenance Tasks

# Check for missing database indices
docker compose exec -u www-data nextcloud php occ db:add-missing-indices

# Run mimetype migrations
docker compose exec -u www-data nextcloud php occ maintenance:mimetype:update-js
docker compose exec -u www-data nextcloud php occ maintenance:mimetype:update-db

# Repair inconsistencies
docker compose exec -u www-data nextcloud php occ maintenance:repair

Backup Strategy

A complete Nextcloud backup requires three components: the PostgreSQL database, the Nextcloud file data volume, and the configuration files. Restoring from only two of three will result in a broken installation.

Component 1: PostgreSQL Database Dump

#!/bin/bash
# backup-db.sh
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="/var/backups/nextcloud"
mkdir -p "$BACKUP_DIR"

docker compose -f /opt/nextcloud/docker-compose.yml exec -T db \
  pg_dump -U nextcloud nextcloud \
  | gzip > "$BACKUP_DIR/nextcloud-db-$DATE.sql.gz"

echo "Database backed up to $BACKUP_DIR/nextcloud-db-$DATE.sql.gz"

Component 2: File Data Volume

For the data volume, enable Nextcloud maintenance mode before snapshotting to ensure consistency:

#!/bin/bash
# backup-data.sh
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="/var/backups/nextcloud"
mkdir -p "$BACKUP_DIR"

# Enable maintenance mode
docker compose -f /opt/nextcloud/docker-compose.yml exec -u www-data nextcloud \
  php occ maintenance:mode --on

# Backup the data volume (using a temporary Alpine container)
docker run --rm \
  -v nextcloud_nextcloud-data:/source:ro \
  -v "$BACKUP_DIR":/backup \
  alpine \
  tar czf "/backup/nextcloud-data-$DATE.tar.gz" -C /source .

# Disable maintenance mode
docker compose -f /opt/nextcloud/docker-compose.yml exec -u www-data nextcloud \
  php occ maintenance:mode --off

echo "Data backed up to $BACKUP_DIR/nextcloud-data-$DATE.tar.gz"

Component 3: Configuration Backup

#!/bin/bash
# backup-config.sh
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="/var/backups/nextcloud"

tar czf "$BACKUP_DIR/nextcloud-config-$DATE.tar.gz" \
  /opt/nextcloud/config/ \
  /opt/nextcloud/.env \
  /opt/nextcloud/docker-compose.yml

echo "Config backed up to $BACKUP_DIR/nextcloud-config-$DATE.tar.gz"

Offsite Backup with rclone

Local backups protect against file corruption and accidental deletion but not server failure. rclone pushes backups to any S3-compatible object storage:

Install and configure rclone:

curl https://rclone.org/install.sh | sudo bash
rclone config
# Follow prompts to add a Backblaze B2 or Wasabi remote named "backup"

Upload backup to object storage:

#!/bin/bash
# backup-offsite.sh
BACKUP_DIR="/var/backups/nextcloud"
REMOTE="backup:your-bucket-name/nextcloud"

# Upload all backup files from today
rclone copy "$BACKUP_DIR" "$REMOTE" \
  --include "*.{gz,tar.gz}" \
  --min-age 0 \
  --transfers 4 \
  --progress

# Remove local backups older than 7 days
find "$BACKUP_DIR" -name "*.gz" -mtime +7 -delete

echo "Offsite backup complete"

Full backup cron (runs at 2 AM daily):

# /etc/cron.d/nextcloud-backup
0 2 * * * root /opt/nextcloud/scripts/backup-db.sh >> /var/log/nextcloud-backup.log 2>&1
10 2 * * * root /opt/nextcloud/scripts/backup-data.sh >> /var/log/nextcloud-backup.log 2>&1
20 2 * * * root /opt/nextcloud/scripts/backup-config.sh >> /var/log/nextcloud-backup.log 2>&1
30 2 * * * root /opt/nextcloud/scripts/backup-offsite.sh >> /var/log/nextcloud-backup.log 2>&1

Backblaze B2 costs: $0.006/GB/month for storage, $0.01/GB for downloads. A 500GB Nextcloud backup costs ~$3/month to store offsite.


Updating Nextcloud

Nextcloud releases minor updates frequently. The Docker image approach makes updates straightforward but requires care with database migrations:

cd /opt/nextcloud

# Pull updated images
docker compose pull

# Enable maintenance mode before updating
docker compose exec -u www-data nextcloud php occ maintenance:mode --on

# Stop and restart with new images
docker compose down
docker compose up -d

# Run database upgrades (Nextcloud handles this automatically on startup)
# Wait for container to complete initialization, then verify
docker compose logs -f nextcloud

# Disable maintenance mode
docker compose exec -u www-data nextcloud php occ maintenance:mode --off

# Run post-upgrade cleanup
docker compose exec -u www-data nextcloud php occ db:add-missing-indices
docker compose exec -u www-data nextcloud php occ upgrade

Never skip major versions. If you're on Nextcloud 27, update to 28 before updating to 29. Nextcloud blocks version skips.


Resource Usage and Scaling

UsersStorageRecommended VPSMonthly Cost
1–5Up to 500GB1 vCPU, 1GB RAM + volume$6–10
5–15500GB–2TB2 vCPU, 2GB RAM + volume$10–20
15–502TB+4 vCPU, 4GB RAM + volume$25–50
50+Multi-TB8 vCPU, 8GB RAM + dedicated storage$80+

Redis significantly reduces database load for multi-user setups. Without Redis, concurrent file sync clients cause PostgreSQL lock contention. With Redis as a file locking backend, 10–15 concurrent users on a 2GB VPS perform well.

For large files (video, archives), configure chunked uploads and ensure Apache/PHP is configured for large upload sizes.


Desktop and Mobile Sync

Nextcloud's official sync clients are the equivalent of the Google Drive desktop app:

  • Desktop: Nextcloud client for Windows, macOS, Linux — selective sync, virtual file system on Windows, bandwidth throttling
  • Mobile: Nextcloud iOS and Android apps — camera auto-upload, offline access, Talk integration

Configure the desktop client to point to https://cloud.yourdomain.com and authenticate with your Nextcloud account. The sync client handles delta sync — only changed portions of modified files are transferred.


Decision Framework

Self-host Nextcloud if:

  • You need more than 2TB of storage and want to avoid per-user SaaS pricing
  • Data must stay within a specific jurisdiction (GDPR, HIPAA, sovereignty requirements)
  • You're replacing Google Drive + Calendar + Contacts for a small team
  • You already run Docker infrastructure and adding a service is routine
  • Budget matters: $6–15/month beats comparable SaaS at scale

Stay on Google Drive/One if:

  • You're a solo user with under 200GB of storage needs (Google One 200GB is $2.99/month)
  • You rely on Google Workspace integrations (Docs in-browser collaborative editing, Gmail, Meet)
  • Server administration is not something you want to manage
  • You need mobile-first workflows with deep OS integration on Android

Consider Nextcloud AIO instead of this manual setup if:

  • You're new to Docker and want the simplest possible setup
  • You don't already run a reverse proxy for other services
  • You want Nextcloud Office (Collabora) without configuring it manually

Self-hosting Nextcloud often goes alongside other infrastructure decisions. Related reads on OSSAlt:


Related: Nextcloud vs Google Workspace Migration 2026 · How to Migrate from Dropbox to Nextcloud 2026 · Self-Hosting Guide: Nextcloud 2026

Comments

The SaaS-to-Self-Hosted Migration Guide (Free PDF)

Step-by-step: infrastructure setup, data migration, backups, and security for 15+ common SaaS replacements. Used by 300+ developers.

Join 300+ self-hosters. Unsubscribe in one click.