Nextcloud on Raspberry Pi: The Complete Docker Compose Guide for 2026
Install Nextcloud 34 on Raspberry Pi 5 with Docker Compose. Step-by-step guide with hardening, backups, and common error fixes for a private cloud.
Introduction
Running your own cloud storage is a key step toward digital independence. Nextcloud gives you file sync, calendar, contacts, and collaborative editing, all under your control. A Raspberry Pi, especially the Pi 5 with its faster CPU and PCIe support, is an excellent low-power host for this. It is silent, costs little to run, and is powerful enough for a household or small team.
This guide walks you through a production-oriented installation of Nextcloud v34.0.3 on a Raspberry Pi using Docker Compose. We will use a stack with MariaDB and Redis for performance and reliability. You will learn how to set up the environment, configure the stack correctly, and avoid the pitfalls that commonly catch new self-hosters.
We will not stop at a basic install. The guide covers reverse proxy setup with Caddy, automated backups, and a section on optional security hardening. By the end, you will have a robust, accessible-from-anywhere cloud service that you fully own. You will also understand the why behind each configuration choice, which is crucial for maintaining your system long-term.
This is a hands-on guide. Every command is copy-paste ready for a fresh Raspberry Pi OS Lite (64-bit) system. We are using the official Nextcloud image at version v34.0.3. While we pin this version, you should always check for newer stable releases on the official Nextcloud download page before a fresh install.
Prerequisites / Hardware & Software Requirements
Before starting, ensure you have the following hardware and software. The Raspberry Pi 5 is the recommended target, but a Pi 4 (4GB or 8GB) will work for smaller setups.
| Component | Minimum Requirement | Recommended | Notes |
|---|---|---|---|
| CPU | ARM Cortex-A72 (Pi 4) | ARM Cortex-A76 (Pi 5) | The Pi 5 significantly reduces response times for encryption and database queries. |
| RAM | 4 GB | 8 GB | Nextcloud, MariaDB, and Redis together typically use 1.5-2.5 GB. More RAM helps with file preview generation. |
| Storage | 32 GB SD card (boot) | 128 GB+ NVMe SSD or USB 3.0 SSD | Critical: Do not use an SD card for data storage. Use an SSD for the data directory and database. SD cards fail quickly under random I/O. |
| Network | Wired 100 Mbps | Wired 1 Gbps | A wired connection is essential for consistent sync performance. |
| Software | Raspberry Pi OS Lite (64-bit) | Raspberry Pi OS Lite (64-bit) | Bookworm or later. The 64-bit OS is required for full compatibility with modern ARM images. |
| Other | Docker & Docker Compose v2 | Docker Engine 27+ & Compose v2 | Install via the official Docker convenience script or apt. |
Initial System Setup
- Update your system
sudo apt update && sudo apt upgrade -y && sudo reboot - Install Docker (using the official script)
Verify the installation.curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh && sudo usermod -aG docker $USER && newgrp dockerdocker --version && docker compose version
Step-by-Step Installation Guide
We will create a dedicated directory for the Nextcloud stack. All files and data will live in ~/nextcloud. We will use a .env file to store secrets and configuration variables, keeping our docker-compose.yml clean and portable.
Step 1: Create Project Directory and Environment File
Create the base directory and navigate into it.
mkdir -p ~/nextcloud && cd ~/nextcloud
Now, create a .env file. This file will hold all your secrets and configurable variables. Never commit this file to a Git repository as it contains your passwords.
nano .env
Paste the following content into the .env file. Replace the placeholder values with strong, unique passwords.
# Database Configuration
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
# Generate a strong password: openssl rand -base64 32
MYSQL_PASSWORD=change_this_db_password
# Root password for MariaDB. Generate a separate one.
MYSQL_ROOT_PASSWORD=change_this_root_password
# Nextcloud Admin User (set during first web install)
NEXTCLOUD_ADMIN_USER=admin
# Generate a strong password: openssl rand -base64 32
NEXTCLOUD_ADMIN_PASSWORD=change_this_admin_password
# Host paths for persistent data - do not change unless you know what you are doing
NEXTCLOUD_DATA_DIR=./data
NEXTCLOUD_CONFIG_DIR=./config
NEXTCLOUD_APPS_DIR=./apps
DB_DATA_DIR=./db
REDIS_DATA_DIR=./redis
# Timezone for your instance
TZ=Europe/Berlin
Save and close the file (Ctrl+X, then Y, then Enter).
Step 2: Create the Docker Compose File
Create a docker-compose.yml file.
nano docker-compose.yml
Paste the complete stack configuration below. This defines three services: nextcloud, db (MariaDB), and redis.
services:
nextcloud:
image: nextcloud:${NEXTCLOUD_VERSION:-v34.0.3}
container_name: nextcloud
restart: unless-stopped
ports:
- "8080:80"
depends_on:
- db
- redis
environment:
- MYSQL_HOST=db
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- REDIS_HOST=redis
- TZ=${TZ}
volumes:
- ${NEXTCLOUD_CONFIG_DIR}:/var/www/html
- ${NEXTCLOUD_DATA_DIR}:/var/www/html/data
- ${NEXTCLOUD_APPS_DIR}:/var/www/html/custom_apps
networks:
- nextcloud_network
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 --skip-innodb-read-only-compressed
volumes:
- ${DB_DATA_DIR}:/var/lib/mysql
environment:
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
networks:
- nextcloud_network
redis:
image: redis:7-alpine
container_name: nextcloud-redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD:-}
volumes:
- ${REDIS_DATA_DIR}:/data
networks:
- nextcloud_network
networks:
nextcloud_network:
driver: bridge
Important Note on Redis: For simplicity, the Redis password is optional. If you leave REDIS_PASSWORD empty in .env, Redis runs without a password. For a local network, this is acceptable. For internet-exposed instances, you should set a strong password here and uncomment the REDIS_PASSWORD line in the nextcloud service environment.
Step 3: Pull and Start the Containers
First, pull the images to ensure you have the latest versions locally. Then start the stack in detached mode.
cd ~/nextcloud && docker compose pull && docker compose up -d
Check the status of the containers.
docker compose ps
All three containers should have a Up status. If any container is Restarting, check the logs with docker compose logs <service_name>.
Step 4: Configure Nextcloud via Web Interface
Open your browser and navigate to http://<your_pi_ip_address>:8080. You will see the Nextcloud setup page.
- Enter the Username and Password you defined in the
.envfile asNEXTCLOUD_ADMIN_USERandNEXTCLOUD_ADMIN_PASSWORD. - Leave the Data folder field as the default value:
/var/www/html/data. This path is mapped to your host's./datadirectory. - Click Install.
Nextcloud will now configure the database and install the core apps. This may take a few minutes. After completion, you will be logged into your new cloud.
Step 5: Post-Installation Configuration
For optimal performance, we need to tell Nextcloud to use Redis for caching and configure the trusted domains.
Edit the Nextcloud configuration file. It is located in your host directory ~/nextcloud/config/config.php.
nano ~/nextcloud/config/config.php
Add the following lines inside the $CONFIG = array ( declaration, before the closing );.
'memcache.local' => '\OC\Memcache\Redis',
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => array(
'host' => 'redis',
'port' => 6379,
),
'trusted_domains' => array (
0 => 'localhost',
1 => '<your_pi_ip_address>',
2 => 'cloud.yourdomain.com', // Add your domain here if you have one
),
'overwrite.cli.url' => 'http://<your_pi_ip_address>:8080',
'htaccess.RewriteBase' => '/',
Replace <your_pi_ip_address> with the actual IP of your Pi. If you have a domain name, add it to the trusted_domains array.
Save the file and restart the Nextcloud container to apply the changes.
cd ~/nextcloud && docker compose restart nextcloud
Step 6: Background Jobs (Cron)
Nextcloud needs to run scheduled tasks (e.g., file scans, expiration of trash). The default is AJAX, which only runs when a user is logged in. We will switch to Cron.
Edit the docker-compose.yml file again.
nano docker-compose.yml
Add the cron service to the file. It uses the same image and volume mounts but runs a cron daemon.
cron:
image: nextcloud:${NEXTCLOUD_VERSION:-v34.0.3}
container_name: nextcloud-cron
restart: unless-stopped
volumes:
- ${NEXTCLOUD_CONFIG_DIR}:/var/www/html
- ${NEXTCLOUD_DATA_DIR}:/var/www/html/data
- ${NEXTCLOUD_APPS_DIR}:/var/www/html/custom_apps
entrypoint: /cron.sh
depends_on:
- db
- redis
networks:
- nextcloud_network
Apply the new configuration.
cd ~/nextcloud && docker compose up -d
Then, set the background job mode to Cron in the Nextcloud admin interface. Go to Settings > Basic settings and select Cron.
Step 7: Set Correct File Permissions
The Nextcloud container runs as www-data (user ID 33). The data and config directories on your host must be writable by this user. Run the following command on your host to set ownership. Do not use chown -R 1000:1000 without checking.
First, verify the user ID the container uses. Run docker exec -it nextcloud id and note the uid and gid (typically 33). Then, apply the correct ownership.
cd ~/nextcloud && sudo chown -R 33:33 data config apps
Step 8: Verify the Installation
Go to Settings > Administration > Overview in the Nextcloud web UI. This page will show any security or setup warnings. Address any critical warnings. A common one is the missing OPcache email, which is fine for a standard install.
Advanced Setup / Optimization
Reverse Proxy with Caddy and SSL
Exposing your Nextcloud directly via port 8080 is not recommended. Use a reverse proxy to handle HTTPS and standard ports. Caddy is a simple choice as it automates SSL certificates.
- Stop the Nextcloud container and remove its port mapping.
cd ~/nextcloud && docker compose stop nextcloud - Edit
docker-compose.ymland remove theports:section from thenextcloudservice. - Create a new directory for the proxy and a
Caddyfile.mkdir -p ~/caddy && cd ~/caddy && nano Caddyfile - Add the following configuration to the
Caddyfile. Replacecloud.yourdomain.comwith your actual domain.cloud.yourdomain.com { reverse_proxy nextcloud:80 } - Create a
docker-compose.ymlfor Caddy.nano docker-compose.ymlservices: caddy: image: caddy:2-alpine container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile - caddy_data:/data - caddy_config:/config networks: - nextcloud_network volumes: caddy_data: caddy_config: networks: nextcloud_network: external: true - Connect your Nextcloud stack's network to Caddy. First, check the network name of your Nextcloud stack.
You will see a network nameddocker network lsnextcloud_nextcloud_network. In the Caddy compose file, we referenced it asexternal: true. Now, start the Caddy container.cd ~/caddy && docker compose up -d - Update the
trusted_domainsandoverwrite.cli.urlinconfig.phpto use your domain withhttps://.
Automated Backups
Back up the data, config, and apps directories, and the database. A simple script can handle this. Create a script in your home directory.
nano ~/backup_nextcloud.sh
Add the following content. Adjust the paths and database credentials.
#!/bin/bash
# Set variables
BACKUP_DIR=~/backups
NEXTCLOUD_DIR=~/nextcloud
DATE=$(date +%Y-%m-%d)
# Create backup directory
mkdir -p $BACKUP_DIR/$DATE
# Backup Nextcloud files (config, data, apps)
sudo rsync -aAXv $NEXTCLOUD_DIR/config $BACKUP_DIR/$DATE/
sudo rsync -aAXv $NEXTCLOUD_DIR/data $BACKUP_DIR/$DATE/
sudo rsync -aAXv $NEXTCLOUD_DIR/apps $BACKUP_DIR/$DATE/
# Backup the database from the running container
docker exec nextcloud-db sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > $BACKUP_DIR/$DATE/db.sql
# Compress the backup
tar -czf $BACKUP_DIR/nextcloud-backup-$DATE.tar.gz -C $BACKUP_DIR $DATE
# Remove the uncompressed directory
rm -rf $BACKUP_DIR/$DATE
# Keep only the last 7 days of backups
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
Make the script executable and run it.
chmod +x ~/backup_nextcloud.sh && sudo ~/backup_nextcloud.sh
Add this script to your crontab to run daily. crontab -e and add 0 2 * * * /home/pi/backup_nextcloud.sh.
Optional Hardening
Warning: The following settings are advanced and can break your container if applied incorrectly. They are specific to security-conscious deployments and may require further adjustments based on your exact setup. Do not copy them blindly into your
docker-compose.ymlwithout understanding the implications.
You can add the following to the nextcloud service to reduce its attack surface. These are not defaults because they can interfere with some apps and update processes.
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- SETUID
- SETGID
- DAC_OVERRIDE
read_only: true
tmpfs:
- /tmp
cap_drop: ALLremoves all Linux capabilities. We then add back only the minimal ones Nextcloud needs to operate (CHOWN,SETUID,SETGID,DAC_OVERRIDE).read_only: truemakes the root filesystem read-only. Nextcloud needs to write to/tmpand its data volumes. We mounttmpfsfor/tmpto allow temporary writes. The data and config directories are already writable via volumes.
Testing is mandatory. After adding these, restart the container and thoroughly test file uploads, app installation, and the web interface.
Troubleshooting Table
| Common Error | Cause | Solution |
|---|---|---|
docker compose up fails with Error starting userland proxy: listen tcp4 0.0.0.0:8080: bind: address already in use |
Port 8080 is already taken by another service on your host. | Change the host port in docker-compose.yml (e.g., 8081:80). Run sudo lsof -i :8080 to identify the process using it. |
Nextcloud web UI shows Internal Server Error after install |
Incorrect file permissions on config/ or data/ directories, or a misconfiguration in config.php. |
Check the logs: docker compose logs nextcloud. Ensure permissions are correct: sudo chown -R 33:33 ~/nextcloud/config ~/nextcloud/data. Review config.php for syntax errors. |
| Nextcloud cannot connect to the database | Wrong credentials or the db service is not running. |
Verify the db container is up: docker compose ps db. Check the credentials in .env match exactly. Check the logs: docker compose logs db. |
Uploads fail with Error while copying file to cache |
The data directory is full or has incorrect ownership. |
Check disk space: df -h. Verify permissions: sudo chown -R 33:33 ~/nextcloud/data. |
| Redis connection error in Nextcloud logs | The Redis container is not running or the password is wrong. | Check docker compose ps redis. If you set a password in the redis service, you must add it to the 'redis' array in config.php. |
cron service is not running or jobs are stuck |
The cron container exited or the Cron mode in Nextcloud settings is not set to Cron. |
Check the cron container logs: docker compose logs cron. Go to Settings > Basic settings and set background job mode to Cron. |
Conclusion
You have successfully deployed Nextcloud v34.0.3 on your Raspberry Pi using Docker Compose. You have a functional private cloud with a database backend, Redis caching, and scheduled background jobs. You have also configured a reverse proxy with automatic SSL and set up a backup routine.
The system is now more reliable and maintainable than a simple manual install. By using Docker, you have isolated the application and its dependencies. The use of environment variables and a Compose file means you can rebuild the entire stack on new hardware in minutes.
Remember that self-hosting is a continuous process. Regularly check for updates to the Nextcloud image and the underlying OS. Monitor your disk usage and logs. The skills you have applied here—understanding services, networking, and security basics—are foundational for any other self-hosted service you might want to run in the future.
FAQ
1. Is a Raspberry Pi 4 sufficient for Nextcloud? Yes, a Raspberry Pi 4 with 4GB or 8GB RAM is sufficient for a personal cloud with a few users. Expect slower response times for preview generation and encryption compared to a Pi 5. For a more responsive experience, especially with multiple concurrent users, the Pi 5 is strongly recommended.
2. How do I update Nextcloud to a new major version?
To update, pull the new image and recreate the containers. First, back up your data and database. Then modify the NEXTCLOUD_VERSION variable in your .env file (or change the tag in docker-compose.yml). Run docker compose pull && docker compose up -d. After the containers start, go to the web UI and follow the upgrade wizard. Always check the official Nextcloud changelog for upgrade paths from your version.
3. Why is Redis necessary? Redis is used for caching file locks, transaction file locking, and distributed caching. It significantly reduces database load and improves performance for concurrent operations. Without it, you will see high database CPU usage and slower response times, especially when multiple clients sync files simultaneously.
4. Can I access my Nextcloud outside my home network?
Yes, you can. The recommended way is to set up a reverse proxy (as shown in this guide) and forward ports 80 and 443 on your router to your Pi. For dynamic IP addresses, use a Dynamic DNS (DDNS) service. Never expose the raw port 8080 directly to the internet without a proxy and SSL.
5. How do I migrate this setup to a new Raspberry Pi?
Migration is straightforward. Install Docker on the new Pi. Copy the entire ~/nextcloud directory (including the .env, docker-compose.yml, config, data, and apps folders) to the new Pi. Then run docker compose up -d. If the database files are copied correctly, your data will be intact. Ensure the user IDs for file ownership match (usually 33).