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+.
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
- Raspberry Pi 4 (4GB+), or
- Mini PC (N100, etc.), or
- VPS (2GB+ RAM)
- Storage: whatever you need for files
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)
- Download from nextcloud.com/install
- Install and launch
- Add account
- Enter your domain:
https://cloud.yourdomain.com - Log in via browser
- 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
- Data directory – Your actual files
- Database – Account info, metadata
- 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
This is the final article in the series
Your cloud. Your data. Your hardware. Welcome to sovereignty.

ReadTheManual is run, written and curated by Eric Lonsdale.
Eric has over 20 years of professional experience in IT infrastructure, cloud architecture, and cybersecurity, but started with PCs long before that.
He built his first machine from parts bought off tables at the local college campus, hoping they worked. He learned on BBC Micros and Atari units in the early 90s, and has built almost every PC he’s used between 1995 and now.
From helpdesk to infrastructure architect, Eric has worked across enterprise datacentres, Azure environments, and security operations. He’s managed teams, trained engineers, and spent two decades solving the problems this site teaches you to solve.
ReadTheManual exists because Eric believes the best way to learn IT is to build things, break things, and actually read the manual. Every guide on this site runs on infrastructure he owns and maintains.
Enjoyed this guide?
New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.

