Nextcloud Docker Setup: Complete Build Guide for Self-Hosting

Nextcloud is the open-source answer to Google Drive, Dropbox, and iCloud. Files, calendar, contacts, notes, photos – all in one platform that you control.

This guide gets you from zero to a working Nextcloud instance in an afternoon. We’ll use Docker for simplicity and include everything you need for production use: SSL, proper database, and backup strategy.

Career Value: This build demonstrates Docker orchestration, reverse proxy configuration, SSL automation, and database management – the exact stack used in enterprise environments. DevOps engineers who can deploy and maintain containerized applications command £55-75k, while those who understand the full infrastructure stack reach £80k+.

Nextcloud architecture diagram showing Docker containers, reverse proxy, and storage layers

What You’ll Build

  • Nextcloud with MariaDB backend
  • Redis for caching
  • Automatic SSL with Let’s Encrypt
  • Desktop and mobile sync
  • Backup automation

Prerequisites

Hardware

Software

  • Docker and Docker Compose
  • Domain name (for SSL)
  • Basic command line familiarity

Network

  • Port 443 accessible (for HTTPS)
  • Static IP or dynamic DNS

Quick Start (30 Minutes)

Step 1: Install Docker

Ubuntu/Debian:

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
# Log out and back in

# Install Docker Compose
sudo apt install docker-compose-plugin

Verify:

docker --version
docker compose version

Step 2: Create Project Directory

mkdir -p ~/nextcloud
cd ~/nextcloud

Step 3: Create Docker Compose File

Create docker-compose.yml:

version: '3.8'

services:
  db:
    image: mariadb:10.11
    container_name: nextcloud-db
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
    volumes:
      - db_data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=${DB_PASSWORD}
    networks:
      - nextcloud-network

  redis:
    image: redis:alpine
    container_name: nextcloud-redis
    restart: unless-stopped
    networks:
      - nextcloud-network

  nextcloud:
    image: nextcloud:latest
    container_name: nextcloud-app
    restart: unless-stopped
    depends_on:
      - db
      - redis
    volumes:
      - nextcloud_data:/var/www/html
      - ${DATA_PATH}:/var/www/html/data
    environment:
      - MYSQL_HOST=db
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=${DB_PASSWORD}
      - REDIS_HOST=redis
      - NEXTCLOUD_ADMIN_USER=${ADMIN_USER}
      - NEXTCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}
      - NEXTCLOUD_TRUSTED_DOMAINS=${DOMAIN}
      - OVERWRITEPROTOCOL=https
    networks:
      - nextcloud-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nextcloud.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.nextcloud.entrypoints=websecure"
      - "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
      - "traefik.http.services.nextcloud.loadbalancer.server.port=80"
      - "traefik.http.middlewares.nextcloud-headers.headers.stsSeconds=31536000"
      - "traefik.http.routers.nextcloud.middlewares=nextcloud-headers"

  traefik:
    image: traefik:v2.10
    container_name: nextcloud-proxy
    restart: unless-stopped
    command:
      - "--api.dashboard=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - letsencrypt_data:/letsencrypt
    networks:
      - nextcloud-network

networks:
  nextcloud-network:
    driver: bridge

volumes:
  db_data:
  nextcloud_data:
  letsencrypt_data:

Step 4: Create Environment File

Create .env:

# Database
DB_ROOT_PASSWORD=YourSecureRootPassword123!
DB_PASSWORD=YourSecureDBPassword123!

# Nextcloud Admin
ADMIN_USER=admin
ADMIN_PASSWORD=YourSecureAdminPassword123!

# Domain (change this!)
DOMAIN=cloud.yourdomain.com
[email protected]

# Data location (external drive, NAS mount, etc.)
DATA_PATH=/mnt/data/nextcloud

Important: Change all passwords and your domain! Never use the example values in production.

Step 5: Create Data Directory

sudo mkdir -p /mnt/data/nextcloud
sudo chown -R www-data:www-data /mnt/data/nextcloud
# Or for Docker: 33:33 (www-data UID:GID)
sudo chown -R 33:33 /mnt/data/nextcloud

Step 6: Configure DNS

Point your domain to your server:

  • A record: cloud.yourdomain.com -> your server’s IP
  • Wait for DNS propagation (check with nslookup cloud.yourdomain.com)

Step 7: Launch

docker compose up -d

Wait 1-2 minutes for initialization. Traefik will automatically get SSL certificate.

Step 8: Access

Visit https://cloud.yourdomain.com

You should see the Nextcloud login. Use the admin credentials from your .env file.

Post-Installation Configuration

Background Jobs

Edit Nextcloud config for better background job handling:

docker exec -it nextcloud-app bash

Inside container:

php occ background:cron
exit

Add cron job on host:

crontab -e

Add:

*/5 * * * * docker exec -u www-data nextcloud-app php cron.php

Memory Caching

Already configured via Redis in our compose file. Verify in admin settings.

Phone Region

docker exec -u www-data nextcloud-app php occ config:system:set default_phone_region --value="GB"

Maintenance Window

docker exec -u www-data nextcloud-app php occ config:system:set maintenance_window_start --type=integer --value=1

Desktop and Mobile Apps

Desktop Sync (Windows, macOS, Linux)

  1. Download from nextcloud.com/install
  2. Install and launch
  3. Add account
  4. Enter your domain: https://cloud.yourdomain.com
  5. Log in via browser
  6. Select folders to sync

Mobile Apps

iOS: Nextcloud app from App Store

Android: Nextcloud app from Play Store or F-Droid

Configure automatic photo upload:

  • Settings -> Auto upload
  • Select camera folder
  • Choose destination folder

Essential Apps to Install

In Nextcloud, go to Apps and install:

Productivity

  • Calendar – CalDAV calendar sync
  • Contacts – CardDAV contact sync
  • Notes – Markdown note taking
  • Tasks – Todo lists

Files

  • Collabora Online or OnlyOffice – Document editing
  • Preview Generator – Better thumbnail generation
  • External Storage – Mount external locations

Media

  • Photos – Photo management
  • Memories – Better photo organization (like Google Photos)

Backup Strategy

What to Back Up

  1. Data directory – Your actual files
  2. Database – Account info, metadata
  3. Config – Your customizations

Automated Backup Script

Create backup.sh:

#!/bin/bash
set -euo pipefail

# Configuration
BACKUP_DIR="/mnt/backup/nextcloud"
DATE=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=7

# Ensure backup directory exists
mkdir -p $BACKUP_DIR

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

# Backup database
docker exec nextcloud-db mysqldump -u root -p"$DB_ROOT_PASSWORD" nextcloud | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"

# Backup data (rsync for efficiency)
rsync -a --delete /mnt/data/nextcloud/ "$BACKUP_DIR/data/"

# Backup config
docker cp nextcloud-app:/var/www/html/config/config.php "$BACKUP_DIR/config_$DATE.php"

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

# Clean old database backups
find $BACKUP_DIR -name "db_*.sql.gz" -mtime +$KEEP_DAYS -delete
find $BACKUP_DIR -name "config_*.php" -mtime +$KEEP_DAYS -delete

echo "Backup completed: $DATE"

Make executable and schedule:

chmod +x backup.sh
crontab -e
# Add: 0 3 * * * /home/user/nextcloud/backup.sh >> /var/log/nextcloud-backup.log 2>&1

Troubleshooting

Can’t Access After Setup

Check containers are running:

docker compose ps

Check logs:

docker compose logs nextcloud
docker compose logs traefik

SSL Certificate Issues

Check Traefik logs:

docker compose logs traefik

Common issues:

  • DNS not pointing to server
  • Port 80/443 blocked by firewall
  • Rate limited by Let’s Encrypt (wait)

Slow Performance

Enable caching (should be on):

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

Preview generation:

docker exec -u www-data nextcloud-app php occ preview:generate-all

Trusted Domain Error

If you see “Access through untrusted domain”:

docker exec -u www-data nextcloud-app php occ config:system:set trusted_domains 0 --value="cloud.yourdomain.com"

Database Connection Issues

Check database is running:

docker compose logs db

Test connection:

docker exec -it nextcloud-db mysql -u nextcloud -p nextcloud

Security Hardening

Fail2ban Integration

Install fail2ban on host:

sudo apt install fail2ban

Create /etc/fail2ban/filter.d/nextcloud.conf:

[Definition]
failregex = ^.*Login failed: .* \(Remote IP: <HOST>\).*$
ignoreregex =

Create /etc/fail2ban/jail.d/nextcloud.local:

[nextcloud]
enabled = true
port = 80,443
protocol = tcp
filter = nextcloud
logpath = /mnt/data/nextcloud/nextcloud.log
maxretry = 3
bantime = 3600

Restart fail2ban:

sudo systemctl restart fail2ban

Regular Updates

Update Nextcloud:

docker compose pull
docker compose up -d

Check for updates in admin panel under Overview.

Update apps: Admin -> Apps -> Updates

Alternative: Simpler Setup Without Traefik

If you already have a reverse proxy (nginx, Caddy), use this simpler compose:

version: '3.8'

services:
  db:
    image: mariadb:10.11
    container_name: nextcloud-db
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
    volumes:
      - db_data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=${DB_PASSWORD}

  redis:
    image: redis:alpine
    container_name: nextcloud-redis
    restart: unless-stopped

  nextcloud:
    image: nextcloud:latest
    container_name: nextcloud-app
    restart: unless-stopped
    depends_on:
      - db
      - redis
    ports:
      - "8080:80"
    volumes:
      - nextcloud_data:/var/www/html
      - ${DATA_PATH}:/var/www/html/data
    environment:
      - MYSQL_HOST=db
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=${DB_PASSWORD}
      - REDIS_HOST=redis
      - NEXTCLOUD_ADMIN_USER=${ADMIN_USER}
      - NEXTCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}
      - NEXTCLOUD_TRUSTED_DOMAINS=${DOMAIN}
      - OVERWRITEPROTOCOL=https

volumes:
  db_data:
  nextcloud_data:

Then proxy to localhost:8080 from your existing reverse proxy.

The Career Connection

This build demonstrates production-grade infrastructure skills that translate directly to enterprise environments:

Skills demonstrated:

  • Docker Compose orchestration
  • Reverse proxy configuration (Traefik)
  • SSL/TLS automation with Let’s Encrypt
  • Database management (MariaDB)
  • Caching layers (Redis)
  • Backup automation and disaster recovery
  • Security hardening (fail2ban)
  • Linux system administration

Interview talking points:

  • Container orchestration and microservices architecture
  • Zero-downtime deployments and maintenance windows
  • Backup strategies and RTO/RPO considerations
  • Security best practices for web applications
  • Monitoring and troubleshooting containerized applications

What You’ve Built

Congratulations. You now have:

  • File storage – Your own Google Drive
  • Photo backup – Your own Google Photos
  • Calendar/Contacts – Your own Exchange
  • Notes – Your own Google Keep
  • Automatic SSL – Proper HTTPS
  • Backups – Protection against data loss

All running on hardware you control, with data you own.

Next Steps

This Week

  • Install apps: Calendar, Contacts, Notes, Memories
  • Set up mobile: Auto-upload photos

This Month

  • Migrate data: Google Takeout -> Nextcloud
  • Test backups: Restore works? Good.

This Quarter

  • Add Collabora/OnlyOffice for document editing
  • Explore Nextcloud Talk for video calls
  • Consider external storage integrations

Continue the Series

Your cloud. Your data. Your hardware. Welcome to sovereignty.

Enjoyed this guide?

New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.

Scroll to Top