Why Nextcloud?

Google Drive is convenient. It is cheap. It works everywhere. But you pay with something other than money: Google scans your files, your photos and your calendar. They use that to show you ads and to understand your habits.

iCloud is better on the privacy front, but you pay for storage every month. And you are still dependent on Apple's infrastructure.

Nextcloud costs nothing to run once you have the hardware. Your photos are yours. Not Google's. The family holiday photos from 2019 are not sitting on a server in Oregon.

It works offline too. When you are home on your LAN, you can access all your files even if the internet is down.

What you need

⚠️ Do not use the SD card for data. Raspberry Pi OS can live on an SD card, but Nextcloud data and the database must be on an SSD. SD cards are not built to handle constant database writes.

Prepare the SSD

Find out what your SSD is called on the system:

lsblk

It will typically be named sda or sdb. Create a filesystem on it (only needed the first time):

sudo mkfs.ext4 /dev/sda1

Create a mount point and mount the SSD:

sudo mkdir -p /data
sudo mount /dev/sda1 /data

To mount it automatically on reboot, add a line to /etc/fstab:

# Find the UUID of the SSD
sudo blkid /dev/sda1

# Add to /etc/fstab (replace UUID with yours)
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  /data  ext4  defaults  0  2

Create the directories Nextcloud needs and set permissions:

sudo mkdir -p /data/nextcloud
sudo chown -R 33:33 /data/nextcloud
📝 User 33: The Nextcloud container runs as www-data, which has UID 33. That is why we set ownership to 33:33.

Docker Compose setup

Create a directory for your Nextcloud installation:

mkdir -p ~/nextcloud
cd ~/nextcloud

Create the file docker-compose.yml:

services:
  db:
    image: mariadb:10.11
    restart: unless-stopped
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: change-this-to-a-strong-password
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: change-this-to-another-strong-password

  nextcloud:
    image: nextcloud:latest
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - /data/nextcloud:/var/www/html/data
      - nextcloud_html:/var/www/html
    environment:
      MYSQL_HOST: db
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: change-this-to-another-strong-password
      NEXTCLOUD_ADMIN_USER: admin
      NEXTCLOUD_ADMIN_PASSWORD: change-this-to-admin-password
      NEXTCLOUD_TRUSTED_DOMAINS: "192.168.1.100 nextcloud.yourdomain.com"
    depends_on:
      - db

volumes:
  db_data:
  nextcloud_html:
⚠️ Use MariaDB, not SQLite. Nextcloud offers SQLite as a lightweight option, but it is not suited for a family's daily use. MariaDB is much faster and more stable, and it is what this guide uses.
💡 Replace the IP address. In NEXTCLOUD_TRUSTED_DOMAINS use your Pi's actual IP address. Find it with hostname -I.

Start Nextcloud

Start the whole stack:

docker compose up -d

The first time this takes a while. Docker pulls the images and Nextcloud sets up the database. Watch what is happening:

docker compose logs -f nextcloud

When you see lines indicating Nextcloud is ready, open a browser and go to:

http://[your-pi-ip]:8080

You should see the Nextcloud welcome screen.

📝 First startup takes time. It can take 2-5 minutes the first time because Nextcloud runs a lot of database setup. Be patient and watch the log.

First setup

Log in with the admin user you set in docker-compose.yml. Nextcloud will ask if you want to install recommended apps. Say yes. This typically includes:

You can always install more apps later under Settings → Apps.

Add family members

Go to the top right corner, click your avatar, and choose Users. Create an account for each family member.

Set a quota for each user. This prevents one family member from accidentally filling the entire SSD with video. 50-100 GB per person is a reasonable starting point.

To share a folder: right-click the folder in the file browser, choose Share, and type the username. You can grant read-only or full write access.

💡 Shared photo album: Create a shared folder called "Family" and give all users access. Now everyone can upload photos to the same album from their phone.

Secure your Pi before going further

Before exposing Nextcloud to the internet, it is worth hardening the Pi itself. Two things matter most:

Remote access with Cloudflare Tunnel

By default, Nextcloud can only be reached from your LAN. If the family wants to use it from their phones while out, you need to expose it.

The safe way is via Cloudflare Tunnel. You open no ports in your router. Cloudflare handles HTTPS. See our guide to Cloudflare Tunnel on Raspberry Pi for the full setup.

If you prefer a VPN over a Cloudflare dependency, WireGuard is a solid alternative. See our WireGuard guide.

Once you have set up the tunnel, you need to add your domain to Nextcloud's trusted domains. Edit config/config.php inside the container:

docker exec -it nextcloud-nextcloud-1 bash
nano /var/www/html/config/config.php

Find trusted_domains and add your domain:

'trusted_domains' =>
  array (
    0 => '192.168.1.100:8080',
    1 => 'nextcloud.yourdomain.com',
  ),
⚠️ Only use Cloudflare Tunnel for external access. Do not open port 8080 directly in your router. Without HTTPS and a WAF in front, exposing Nextcloud directly to the internet is not safe.

Mobile app

The Nextcloud app is available for both iOS and Android and is free.

Backup

A Pi can fail. An SSD can die. Backup is not optional.

The simple approach: stop Nextcloud, copy the data, start again.

# Stop the containers
cd ~/nextcloud
docker compose stop

# Create a database dump
docker exec nextcloud-db-1 sh -c \
  'mysqldump --all-databases -u root -p"your-root-password"' \
  > backup-db-$(date +%Y%m%d).sql

# Copy Nextcloud data to an external disk (adjust the path)
rsync -av /data/nextcloud/ /mnt/backup/nextcloud-data/

# Start the containers again
docker compose start

Automate it with cron. Run crontab -e and add:

# Run backup every Sunday at 02:00
0 2 * * 0 cd ~/nextcloud && docker compose stop && rsync -aq /data/nextcloud/ /mnt/backup/nextcloud/ && docker compose start
💡 3-2-1 backup: Nextcloud is local storage. A fire, theft or SSD failure can wipe everything. Copy the backup to at least one external location, such as an external drive at a family member's house or an encrypted cloud backup.

Performance tips

Nextcloud on a Pi 4 is perfectly usable for a family, but a few things make it noticeably faster.

Redis for memory cache

Add Redis to your docker-compose.yml:

  redis:
    image: redis:alpine
    restart: unless-stopped

Then add these environment variables to the nextcloud service:

      REDIS_HOST: redis

And add redis to depends_on in the nextcloud service. Redis keeps Nextcloud's session data and lock information in memory, which takes pressure off the database. The difference in response times is noticeable.

PHP memory and upload limits

Add these environment variables to the nextcloud service:

      PHP_MEMORY_LIMIT: 512M
      PHP_UPLOAD_LIMIT: 512M

Cron job for background tasks

Nextcloud has background tasks that need to run regularly (indexing, thumbnails, etc.). Add a cron job on the Pi:

# Run Nextcloud cron every 5 minutes
*/5 * * * * docker exec -u www-data nextcloud-nextcloud-1 php -f /var/www/html/cron.php

Switch to Cron (not AJAX) in Nextcloud under Settings → Basic settings.

What Nextcloud is not

Nextcloud is not a cloud backup. It is local storage with a web interface. If the house burns or the Pi is stolen, the data is gone unless you have an external backup.

A Raspberry Pi 4 is not built to handle 100 concurrent users. For a family of 4-6 people it works well. If you want to run it for a whole office, you need more powerful hardware.

It is not Google Photos either. Nextcloud Photos is good, but it does not recognise faces or locations automatically the way Google's AI does. That is the price of privacy.

Sources