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
- Create a project directory:
mkdir -p ~/nextcloud && cd ~/nextcloud
- Create
.envfor 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
- 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
- Start the stack:
docker compose up -d
- Watch logs to ensure the database initializes:
docker compose logs -f db
Wait for the “mariadbd: ready for connections” line, then press Ctrl+C.
- Open your browser:
http://your-server-ip:8080
- 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:
- Create an admin account
- Database: MySQL/MariaDB
- Database user:
nextcloud - Database password: (from
.env) - Database name:
nextcloud - Database host:
db -
Click Install
-
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
- Enter the Nextcloud app container:
docker exec -u www-data -it nextcloud-app bash
- 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
- Increase PHP memory limit:
php occ config:system:set memory_limit --value=1024M
- Set background jobs to use cron (already running in our
cronservice):
php occ background:cron
- Exit the container:
exit
- 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_onwithcondition: service_healthyhandles this) - Verify
.envpasswords 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
croncontainer 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
- Compare Nextcloud vs Immich for photo backup
- Add OnlyOffice or Collabora for document editing
- Back up your volumes with Restic or BorgBackup
- Set up client-side encryption with Nextcloud E2EE
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-owncloudfor readers comparing cloud platformsperformance→/docker-monitoring-grafana-prometheusto monitor Nextcloud resource usageconclusion→/nextcloud-immich-comparisonfor 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.