Reading time: ~16 minutes
Audience: Docker users ready to replace Google Drive / Dropbox / iCloud
Last tested: Nextcloud 30.0, MariaDB 11.4, Redis 7.4, June 2026


What Is Nextcloud?

Overview

Nextcloud is a self-hosted cloud storage and collaboration platform. It provides file sync, document editing (via Collabora or OnlyOffice), calendar/contacts sync (CalDAV/CardDAV), and a growing app ecosystem — all under your control, on your server, with no vendor lock-in or data mining.

For homelab users, Nextcloud is often the first “serious” self-hosted application. It replaces multiple SaaS subscriptions and gives you terabytes of storage without monthly fees.

Key Benefits

Feature Nextcloud Self-Hosted Google Drive / Dropbox
Storage cost One-time hardware $10–30/month for 2 TB+
Data privacy You own the encryption keys Provider can scan files
Custom domain Yes Business plans only
Apps / integrations 200+ community apps Limited to vendor choices
Max file size Limited only by disk/ZFS 750 GB upload limit
Offline sync Desktop, Android, iOS Desktop, Android, iOS

Prerequisites

Hardware Requirements

Users RAM CPU Storage Notes
1–2 personal 2 GB 2 cores 100 GB SSD Raspberry Pi 5 or N100 viable
3–5 family 4 GB 4 cores 500 GB SSD + HDD backup Recommended for most homelabs
10+ small team 8 GB 4 cores 1 TB NVMe + backup Redis and database on same host

Software Requirements

  • Docker Engine 24.0+ and Docker Compose plugin
  • A Linux host with a static IP or local DNS name
  • (Optional) A reverse proxy for HTTPS (NGINX Proxy Manager, Traefik, or Caddy)

Step 1: Create the Docker Compose Stack

Objective

Write a production-grade docker-compose.yml with MariaDB, Redis, and Nextcloud.

Step-by-Step Instructions

  1. Create a project directory:
mkdir -p ~/nextcloud && cd ~/nextcloud
  1. Create .env for secrets:
cat > .env << 'EOF'
MYSQL_ROOT_PASSWORD=change-this-root-password-now
MYSQL_PASSWORD=change-this-db-password-now
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=change-this-admin-password-now
NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.yourdomain.com
EOF
chmod 600 .env
  1. Create docker-compose.yml:
version: "3.8"

services:
  db:
    image: mariadb:11.4
    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=${MYSQL_ROOT_PASSWORD}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
    healthcheck:
      test: ["CMD", "mariadb-admin", "ping", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7.4-alpine
    container_name: nextcloud-redis
    restart: unless-stopped
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru

  app:
    image: nextcloud:30-apache
    container_name: nextcloud-app
    restart: unless-stopped
    ports:
      - "8080:80"
    links:
      - db
      - redis
    volumes:
      - nextcloud-data:/var/www/html
    environment:
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_HOST=db
      - REDIS_HOST=redis
      - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER}
      - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
      - NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_TRUSTED_DOMAINS}
      - OVERWRITEPROTOCOL=https
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/status.php"]
      interval: 30s
      timeout: 10s
      retries: 3

  cron:
    image: nextcloud:30-apache
    container_name: nextcloud-cron
    restart: unless-stopped
    volumes:
      - nextcloud-data:/var/www/html:ro
    entrypoint: /cron.sh
    depends_on:
      - db
      - redis

volumes:
  db-data:
  redis-data:
  nextcloud-data:

Architecture Note: We use MariaDB instead of PostgreSQL because it is more forgiving for beginners and widely tested with Nextcloud. Redis acts as a file locking cache, dramatically improving performance for concurrent users.


Step 2: Deploy and Complete Web Setup

Objective

Start the stack and finish installation through the web UI.

Step-by-Step Instructions

  1. Start the stack:
docker compose up -d
  1. Watch logs to ensure the database initializes:
docker compose logs -f db

Wait for the “mariadbd: ready for connections” line, then press Ctrl+C.

  1. Open your browser:
http://your-server-ip:8080
  1. You should see the Nextcloud setup screen. Because we passed admin credentials via environment variables, it may auto-create the admin account and skip the wizard. If not:
  2. Create an admin account
  3. Database: MySQL/MariaDB
  4. Database user: nextcloud
  5. Database password: (from .env)
  6. Database name: nextcloud
  7. Database host: db
  8. Click Install

  9. After installation, log in and go to Administration settings → Overview. Address any warnings (HTTPS, memory cache, etc.).


Step 3: Configure Redis and Performance

Objective

Enable Redis for file locking and adjust PHP memory limits.

Step-by-Step Instructions

  1. Enter the Nextcloud app container:
docker exec -u www-data -it nextcloud-app bash
  1. Configure Redis in Nextcloud:
php occ config:system:set redis host --value=redis
php occ config:system:set redis port --value=6379
php occ config:system:set memcache.locking --value=\\OC\\Memcache\\Redis
php occ config:system:set memcache.local --value=\\OC\\Memcache\\APCu
  1. Increase PHP memory limit:
php occ config:system:set memory_limit --value=1024M
  1. Set background jobs to use cron (already running in our cron service):
php occ background:cron
  1. Exit the container:
exit
  1. Restart the app container to apply:
docker compose restart app

Step 4: Reverse Proxy and HTTPS

Objective

Secure Nextcloud with HTTPS via NGINX Proxy Manager.

Step-by-Step Instructions

If you already have NGINX Proxy Manager running, add a Proxy Host:

Field Value
Domain Names nextcloud.yourdomain.com
Scheme http
Forward Hostname / IP nextcloud-app
Forward Port 80
Block Common Exploits ✅ Enabled
Websockets Support ✅ Enabled

Important Nextcloud config for reverse proxy:

Enter the app container and run:

php occ config:system:set trusted_proxies 0 --value=172.18.0.0/16
php occ config:system:set overwritehost --value=nextcloud.yourdomain.com
php occ config:system:set overwriteprotocol --value=https
php occ config:system:set overwrite.cli.url --value=https://nextcloud.yourdomain.com

Security Note: Do not expose port 8080 directly to the internet. Always use a reverse proxy with valid SSL certificates.


Step 5: Desktop and Mobile Sync Setup

Objective

Connect client apps to your self-hosted instance.

Step-by-Step Instructions

Desktop (Windows/macOS/Linux): 1. Download Nextcloud Desktop from nextcloud.com/install 2. Enter your server URL: https://nextcloud.yourdomain.com 3. Log in and choose folders to sync

Android / iOS: 1. Install Nextcloud from the Play Store / App Store 2. Enter server URL and credentials 3. Enable auto-upload for camera photos (a popular Immich alternative for simple sync)

CalDAV/CardDAV (Thunderbird, Apple Calendar, DAVx⁵): - URL: https://nextcloud.yourdomain.com/remote.php/dav - Use your Nextcloud username and app password (create one in Security → Devices & sessions)


Pro Tips

Tip 1: Enable Server-Side Encryption

If your data disk is not encrypted at rest, enable Nextcloud’s server-side encryption:

php occ encryption:enable
php occ encryption:encrypt-all

Note: This protects data at rest but adds CPU overhead. ZFS native encryption (zfs create -o encryption=on) is often a better alternative.

Tip 2: Offsite Backup with Restic

Back up your nextcloud-data and db-data volumes nightly:

docker exec nextcloud-db mariadb-dump -u root -p${MYSQL_ROOT_PASSWORD} nextcloud > nextcloud-sqlbkp_$(date +%Y%m%d).sql

Store the SQL dump and volume backup on a Hetzner Storage Box or local NAS.

Tip 3: Use Preview Generator

Generate thumbnails ahead of time to avoid CPU spikes when browsing photos:

php occ app:install previewgenerator
php occ preview:generate-all

Add to cron for periodic generation.

Tip 4: Limit Upload Size

The default upload limit is 2 GB. To raise it:

php occ config:system:set max_chunk_size --value=104857600

Also adjust the reverse proxy (client_max_body_size 10G; in NGINX).


Troubleshooting Common Issues

Internal Server Error” on First Login

  • Check database connectivity: docker compose logs db
  • Ensure MariaDB finished initializing before Nextcloud started (our depends_on with condition: service_healthy handles this)
  • Verify .env passwords do not contain special characters that break YAML parsing (wrap in quotes if needed)

Slow File Listing

  • Ensure Redis is configured for file locking (see Step 3)
  • Enable PHP OPcache in the container (it is enabled by default in the official image)
  • Check that cron container is running: docker compose ps cron

Trusted Domain” Error

Add your domain to NEXTCLOUD_TRUSTED_DOMAINS in .env, then:

docker compose down
docker compose up -d

Conclusion

Summary

You now have a production-grade Nextcloud deployment with MariaDB, Redis, cron jobs, and reverse proxy compatibility. It can serve a family or small team with file sync, calendars, contacts, and optional document editing.

Next Steps


Affiliate Opportunities

  • Storage: Samsung 870 EVO / 990 PRO SSDs for Nextcloud data volumes
  • Mini PCs: Intel N305 systems for running Nextcloud 24/7 at low power
  • UPS: APC Back-UPS to prevent database corruption during power loss

Internal Linking Strategy

  • what-is/nextcloud-vs-owncloud for readers comparing cloud platforms
  • performance/docker-monitoring-grafana-prometheus to monitor Nextcloud resource usage
  • conclusion/nextcloud-immich-comparison for photo backup specialization

CTA

What did you replace with Nextcloud? Share your self-hosting wins in the comments!

Subscribe to the WordForge newsletter for weekly Docker and self-hosted app guides.