Running Docker on Windows WSL2/Ubuntu (Part 7) – Perfect Installation of Nextcloud
You can synchronize files across devices, back up your smartphone’s photos automatically, share documents securely, and even collaborate with colleagues on office files. By the end of this guide, you’ll have Nextcloud running on your WSL2 Ubuntu home server
If you’ve been following this series, we’ve already set up a home server stack on Windows WSL2 with Ubuntu. We installed Docker, configured Nginx Proxy Manager (NPM), and deployed media servers like Jellyfin. Now, it’s time to build something even more powerful: your own private cloud storage and collaboration platform with Nextcloud.
Think of Nextcloud as a self-hosted Dropbox, Google Drive, and Google Photos alternative—all in one. You can synchronize files across devices, back up your smartphone’s photos automatically, share documents securely, and even collaborate with colleagues on office files. By the end of this guide, you’ll have Nextcloud running on your WSL2 Ubuntu home server, accessible via a clean domain name like:
👉 https://nextcloud.yourname.dnsduck.org
Why Nextcloud?
- File Sync & Share: Works across Windows, macOS, Linux, iOS, and Android.
- Mobile Photo Backup: Automatically upload your camera roll to your server, replacing Google Photos.
- Collaboration Tools: Whiteboards, chat, calendars, contacts, and more.
- Privacy: Your files, your server, your rules.
- Scalability: From a personal NAS setup to enterprise-grade clusters.
Step 1: Prepare Folder Structure
Inside your WSL2 home directory (e.g., ~/docker/nextcloud), create the following folders:
nextcloud/
├── docker-compose.yaml
├── php/custom.ini
├── mariadb/
│ ├── data/
│ └── conf.d/
├── nextcloud/
│ ├── html/
│ ├── config/
│ └── data/
└── redis/This separation ensures your database, Nextcloud config, and Redis cache are persistent and easy to back up.
Step 2: docker-compose.yaml
Here’s a production-ready Docker Compose file for Nextcloud.
services:
nextcloud-db:
image: mariadb:11.4
container_name: nextcloud-db
restart: unless-stopped
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --innodb-file-per-table=1
environment:
- MYSQL_ROOT_PASSWORD=your_strong_password
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=your_strong_password
- TZ=Asia/Seoul
volumes:
- ./mariadb/data:/var/lib/mysql
- ./mariadb/conf.d:/etc/mysql/conf.d
networks:
- nextcloud-internal
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 10s
retries: 20
start_period: 40s
logging:
driver: json-file
options:
max-size: "10m"
max-file: 3
nextcloud-app:
image: nextcloud:latest
container_name: nextcloud-app
restart: unless-stopped
depends_on:
nextcloud-db:
condition: service_healthy
nextcloud-redis:
condition: service_healthy
environment:
- MYSQL_HOST=nextcloud-db
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=your_strong_password
- REDIS_HOST=nextcloud-redis
- REDIS_HOST_PORT=6379
- NEXTCLOUD_ADMIN_USER=admin
- NEXTCLOUD_ADMIN_PASSWORD=your_strong_password
- NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.yourname.dnsduck.org
- TZ=Asia/Seoul
volumes:
- ./nextcloud/html:/var/www/html
- ./nextcloud/config:/var/www/html/config
- ./nextcloud/data:/var/www/html/data
- ./php/custom.ini:/usr/local/etc/php/conf.d/custom.ini
networks:
- edge
- nextcloud-internal
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/"]
interval: 30s
timeout: 10s
retries: 5
start_period: 40s
logging:
driver: json-file
options:
max-size: "10m"
max-file: 3
nextcloud-redis:
image: redis:alpine
container_name: nextcloud-redis
restart: unless-stopped
volumes:
- ./redis:/data
networks:
- nextcloud-internal
healthcheck:
test: ["CMD", "redis-cli", "-h", "localhost", "ping"]
interval: 30s
timeout: 10s
retries: 5
start_period: 20s
logging:
driver: json-file
options:
max-size: "10m"
max-file: 3
networks:
edge:
external: true
nextcloud-internal:
driver: bridge
Step 3: PHP Configuration (custom.ini)
Create a file custom.ini in php directory:
memory_limit=1G
upload_max_filesize=10G
post_max_size=10G
max_execution_time=36000
output_buffering=0
This ensures large uploads and long-running tasks (like syncing gigabytes of files) won’t fail.
Step 4: Bring Up the Stack
Run:
docker compose up -d
Check logs:
docker logs -f nextcloud-app
Once the containers are healthy, go to:
👉 https://nextcloud.yourname.dnsduck.org
Login with the admin credentials you set in docker-compose.yaml.
Step 5: Fix Security & Setup Warnings (Detailed)
Nextcloud performs a self-check after installation. These fixes make your instance secure and reverse-proxy-aware when running behind Nginx Proxy Manager (NPM).
1) Configure Trusted Proxies & HTTPS
Goal: Tell Nextcloud it’s sitting behind a reverse proxy (NPM) and that external traffic arrives over HTTPS, even if NPM forwards to Nextcloud over HTTP internally.
1.1 Find your edge network and NPM IP/subnet
# Edge network subnet (CIDR)
docker network inspect edge --format '{{(index .IPAM.Config 0).Subnet}}'
# Example output: 172.18.0.0/16
# NPM container IP (optional, for sanity check)
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' npm-edge
# Example: 172.18.0.3
If you’re using the shared edge network, using the whole subnet is practical:
trusted_proxies→['172.18.0.0/16'](or whatever youredgesubnet is)
1.2 Apply the reverse-proxy aware settings
Edit file (what you already had):
sudo nano ./nextcloud/config/config.php
Add/update:
'trusted_proxies' => ['172.18.0.0/16'], // adjust to your edge subnet
'trusted_domains' => ['nextcloud.yourname.dnsduck.org'], // if not already set
'overwritehost' => 'nextcloud.yourname.dnsduck.org',
'overwrite.cli.url' => 'https://nextcloud.yourname.dnsduck.org',
'overwriteprotocol' => 'https',
'overwritecondaddr' => '^172\\.18\\.', // matches proxy source addresses
'default_phone_region'=> 'KR',
1.3 Ensure NPM forwards the right headers
In NPM → Proxy Hosts → your nextcloud.yourname.dnsduck.org host:
- SSL: use a valid Let’s Encrypt cert, Force SSL, HTTP/2 enabled.
- Advanced → Custom Nginx Configuration (add these if not present):
# Forwarded headers for apps behind reverse proxy
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
These help Nextcloud understand original scheme/host/IP coming through NPM.
1.4 Clear caches & confirm
docker exec -it nextcloud-app bash -lc "php occ maintenance:repair"
docker exec -it nextcloud-app bash -lc "php occ config:system:get overwriteprotocol"
You should now see the HTTP/HTTPS warning disappear in Admin → Overview.
2) Set Maintenance Window (avoid heavy jobs in peak time)
Why: Nextcloud warns if you haven’t set a maintenance window. This is when intensive jobs (e.g., previews, file scans, mimetype updates) are allowed to run.
Set it (e.g., 3 AM):
docker exec -it nextcloud-app bash -lc \
"php occ config:system:set maintenance_window_start --type=integer --value=3"
3) Run MimeType Migration (can be slow)
Why: When new mimetypes are added, Nextcloud asks you to run an “expensive” repair. It’s normal on fresh installs (and on upgrades).
docker exec -it nextcloud-app bash -lc \
"php occ maintenance:repair --include-expensive"
Let it finish. It may take a while if you already imported lots of files.
4) Configure Email Notifications (and test)
In Admin Settings → Basic settings → Email server:
- Send mode: SMTP
- Server:
mail.yourdomain.com(or provider’s SMTP) - Port:
465(SSL) or587(STARTTLS) - Authentication: your SMTP username/password
- From address:
notifications@yourdomain.com(or similar)
Click “Send email” to test.
Recommended Extras (commonly flagged by the Scanner)
These aren’t always listed in your warnings, but they’re best practice and often remove yellow notices.
A) Enforce HSTS (after HTTPS works)
Why: Nextcloud may warn that Strict-Transport-Security isn’t set. Enable HSTS in NPM (Proxy Host → SSL):
- Check “HSTS Enable”
- Set max-age ≥
15552000(180 days) - Consider Include subdomains if your subdomains are all HTTPS.
Turn this on only after you’re sure HTTPS works everywhere (HSTS is “sticky” in browsers).
B) Background jobs: switch from AJAX to Cron
Why: AJAX background jobs are unreliable on low-traffic sites. Cron is recommended and will quiet related warnings.
Option 1 (host Task Scheduler / cron)
Create a Windows Task (or Linux cron on the host) that runs:
wsl -d Ubuntu-24.04 -- bash -lc "docker exec -u www-data nextcloud-app php -f /var/www/html/cron.php"
Run every 5 minutes.
Option 2 (simple containerized cron)
You can also run a tiny cron container on the internal network that executes the same command; but keeping it on the host (Task Scheduler) is usually simpler in WSL.
Verify in Admin → Basic settings → Background jobs that Cron is selected.
C) Validate Redis/APCu
You already configured APCu/Redis; to confirm:
- Admin → Overview → System should show no file locking warnings.
- Or CLI:
docker exec -it nextcloud-app bash -lc "php occ config:list system | egrep -i 'memcache|redis'"
D) “Strict cookie check” warnings
If you see “Request does not pass strict cookie check” in logs after flipping domains:
- Ensure you only access via your HTTPS domain (not IP or a different host).
- Clear browser cookies for your domain, then log in again.
Quick Validation Checklist
- Admin → Overview has no HTTP / HSTS / proxy warnings.
- Visiting
http://nextcloud.yourname.dnsduck.orgredirects to HTTPS. - HSTS enabled in NPM (after confirming HTTPS is stable).
- Background jobs set to Cron; Task Scheduler job runs every 5 minutes.
- Email test succeeds.
occ maintenance:repair --include-expensivecompleted successfully.trusted_domains,trusted_proxies, andoverwrite*values are correct.
Troubleshooting Tips
- Still seeing the “You are accessing over HTTP” warning?
Double-check:overwriteprotocolishttpsoverwritehostmatches your public host exactly- NPM is forwarding X-Forwarded-Proto and X-Forwarded-Host
- You aren’t visiting via IP or an alternate host
- Looping redirects / 400 errors
Remove any stale HSTS from the browser (or test in a private window), and verify there’s no conflicting redirect in NPM’s Advanced section. - Emails fail with Gmail
Use an App Password (Google Account → Security → App passwords) and set:mail_smtphost=smtp.gmail.commail_smtpport=465+mail_smtpsecure=ssl- Username = full Gmail address, Password = App Password
With these changes, your Nextcloud “Security & setup warnings” page should be quiet—and your instance will behave correctly behind NPM with clean HTTPS and proper proxy awareness.
Step 6: Verify Redis Cache
Redis is critical for Nextcloud performance. It reduces database load by handling file locking, distributed caching, and speeding up metadata lookups. If Redis is not configured correctly, you’ll often see “file is locked” errors or sluggish performance under concurrent use.
6.1 Check from the Admin UI
- Log into Nextcloud as an admin
- Go to: Admin Settings → Overview → System
- Scroll down to System Status
- ✅ If configured correctly, you should see:
- “Redis” listed under Caching
- No warnings about “Transactional file locking is disabled”
6.2 Check via CLI (recommended)
Run this inside your Nextcloud container:
docker exec -it nextcloud-app bash -lc "php occ config:list system | egrep -i 'memcache|redis'"
Expected output (example):
"memcache.local": "\\OC\\Memcache\\APCu"
"memcache.locking": "\\OC\\Memcache\\Redis"
"memcache.distributed": "\\OC\\Memcache\\Redis"
"redis": {
"host": "nextcloud-redis",
"port": 6379,
"password": ""
}
If you see all three memcache.* values pointing to Redis/APCu and your Redis host matches your Docker Compose service name → you’re good.
6.3 Test Redis Responsiveness
docker exec -it nextcloud-redis redis-cli ping
If Redis is healthy, you’ll get:
PONG
6.4 Why Redis Matters
- ⚡ Faster performance under high load
- 🔒 Prevents “file locking” errors during concurrent edits
- 🗂 Essential for apps like Collabora, OnlyOffice, or large file shares
If Redis isn’t working, Nextcloud falls back to database-based locking, which is far slower and less reliable.
Step 7: Daily Usage & Mobile Apps
Nextcloud isn’t just a web UI. To fully benefit, you’ll want desktop and mobile integration.
7.1 Desktop Client
- Available for Windows, macOS, and Linux
- Download from: https://nextcloud.com/install
- After installation:
- Add a new account
- Enter your server URL:
👉https://nextcloud.yourname.dnsduck.org - Log in with your admin (or personal) user credentials
Features:
- 🔄 Continuous file sync (like Dropbox or OneDrive)
- 📁 Selective Sync: choose which folders sync locally
- ⏱ Conflict handling when editing files across multiple devices
7.2 Mobile Apps
- Install the official Nextcloud app:
Setup:
- Open the app → enter server URL:
👉https://nextcloud.yourname.dnsduck.org - Log in with your Nextcloud credentials
Key Features:
- 📸 Automatic Photo & Video Uploads
- Enable in Settings → Auto Upload
- Choose “Camera folder” or any folder you like
- Options:
- Upload on Wi-Fi only
- Upload while charging only
- Upload videos (optional, since they’re larger)
- 📂 File browsing & offline access
- 🔔 Push notifications (e.g., shares, calendar invites)
- ✏️ Basic document preview/edit support
7.3 Extra Apps for Mobile Integration
- Nextcloud Notes → sync notes across devices
- Nextcloud Talk → secure chat & video calls (requires server app enabled)
- Nextcloud Deck → Kanban board productivity app
- DAVx⁵ (Android only) → sync Nextcloud Calendar & Contacts with native phone apps
7.4 Best Practices for Daily Use
- Always connect via HTTPS to avoid security warnings
- Keep your desktop/mobile clients updated for the latest bug fixes
- Use separate user accounts for family/team members instead of sharing admin credentials
- Regularly back up your
./nextcloud/dataand database volumes (cron job or rsync recommended)
👉 With Redis caching verified and desktop/mobile clients configured, you now have a personal cloud platform that rivals Dropbox, Google Drive, and iCloud — but fully under your control.
Step 8: Backup Strategy
A reliable backup strategy is the most important part of running a self-hosted Nextcloud server. Disks fail, containers break, and sometimes a single mistyped command can wipe everything. If you don’t have backups, you don’t have a cloud — you have a ticking time bomb.
8.1 What to Back Up
At a minimum, you must back up these three volumes:
./nextcloud/config- Contains all system configuration files, trusted domains, overwrite rules, email settings, Redis setup, and installed apps list.
- Without this, you’ll lose your server identity and configuration.
./nextcloud/data- The heart of Nextcloud: all uploaded files, user data, profile pictures, thumbnails, and previews.
- If this folder is lost, your cloud storage is gone.
./mariadb/data- Stores the entire Nextcloud database: users, shares, permissions, calendar events, contacts, and file index.
- Even if you have the raw files in
./data, Nextcloud needs the DB to know which file belongs to which user.
👉 Optional but recommended:
./php/custom.ini→ keeps your PHP settings (upload limits, memory)../redis→ Redis cache folder (less critical, can be rebuilt if lost).
8.2 Manual Backup (Quick & Easy)
From the host machine:
# Stop containers to ensure data consistency
docker compose down
# Backup config, data, and DB
tar -czf nextcloud-backup-$(date +%F).tar.gz \
./nextcloud/config \
./nextcloud/data \
./mariadb/data
# Start containers again
docker compose up -d
This creates a compressed snapshot of your instance with the date in the filename. Store it on a second disk, NAS, or cloud storage.
8.3 Automated Backup with Cron
You don’t want to rely on memory for backups. Let cron handle it.
Example cron job (crontab -e):
0 3 * * * /usr/bin/docker exec -t nextcloud-db \
mysqldump -u nextcloud -p'your_strong_password' nextcloud \
> /backups/nextcloud-db-$(date +\%F).sql
0 4 * * * tar -czf /backups/nextcloud-files-$(date +\%F).tar.gz \
/path/to/nextcloud/config \
/path/to/nextcloud/data
- Database dump every night at 3 AM
- Files + config backup every night at 4 AM
- Store in
/backups(mounted to an external disk or NAS)
8.4 Backup with a Dedicated Container
You can also use a backup container such as Duplicati, BorgBackup, or Restic.
- These tools support incremental backups, encryption, and remote storage (Google Drive, S3, Backblaze, etc.).
- Mount your Nextcloud volumes inside the backup container and let it handle scheduling, deduplication, and retention.
8.5 Recovery Strategy
A backup is only good if you can restore it. Test it!
- Spin up a temporary Nextcloud instance on another machine or VM.
- Copy your backup into the correct folders.
- Start Nextcloud and verify files, users, and settings appear.
Restore the DB dump:
docker exec -i nextcloud-db mysql -u root -p'your_strong_password' nextcloud < backup.sql
8.6 Best Practices
- 📅 Frequency: Daily incremental + weekly full backups.
- 🌍 Offsite storage: Don’t keep all backups on the same server. Use a NAS, external drive, or cloud bucket.
- 🔐 Encryption: Encrypt backups if they leave your LAN.
- ✅ Testing: Schedule quarterly restore tests to ensure your process works.
👉 With a solid backup strategy in place, you can sleep peacefully knowing that your Nextcloud instance — files, users, and data — can be recovered even if disaster strikes.
Conclusion
Congratulations 🎉! You now have a fully functional Nextcloud instance running on Docker under WSL2 Ubuntu. With MariaDB, Redis, custom PHP settings, and reverse proxy via NPM, your server is ready to act as a personal Dropbox, Google Drive, and Google Photos replacement.