
Free cloud storage fills up quietly, and phone photo backup fails in the most forgettable way there is. The account crosses its limit, the app puts a small warning icon somewhere nobody looks, and uploading simply stops. Weeks later somebody goes hunting for a picture, does not find it, and works out that a month and a half of photographs exist on exactly one device. Nothing is lost while that handset survives. That is the entire margin: one dropped phone. Self-hosting is the other answer to the same problem, and Nextcloud is the shape it usually takes — your own drive, your own photo backup, running on a machine in your own flat, limited by the disks you bought rather than a plan you did not choose. It is both better and more work than most write-ups admit, and the work does not arrive on day one. It arrives in month four, which is why this guide spends as long on backups and background jobs as on installation.
what you get, and what you take on

Nextcloud is the self-hosted equivalent of a cloud drive. Files sync between laptop and server, phones upload photos in the background, you can share a link, and there are apps for calendars, contacts and notes on top. Storage is limited by the drives you own rather than a plan, which is the appeal once a family photo library outgrows a free tier.
The part most guides skate over is the trade. You are not removing a dependency, you are moving it onto yourself. Nobody else patches this server, or notices that the disk is nearly full, or that the nightly backup stopped running a fortnight ago. That trade is worth making, but make it with your eyes open rather than discovering it in month four.
Self-hosting your photos without a working, tested backup is worse than the cloud service you left. A single drive in your flat is one failure away from nothing.
install approaches and their trade-offs


There are four routes people take, and the right one depends mostly on how much you want to understand versus how quickly you want to be finished.
| Approach | Good for | Catch |
|---|---|---|
| All-in-One container | Fastest working install, handles updates and backup for you | Wants control of its own stack; awkward to slot into an existing proxy setup |
| Official image plus your own Compose file | Full control, fits an existing reverse proxy | You wire up database, cache and cron yourself |
| Snap package | Simple on Ubuntu, self-updating | Opinionated paths, less room to tune |
| Source install on a LAMP stack | Maximum control, best for learning | Most maintenance, most ways to get it subtly wrong |
I would reach for the official image inside your own Compose project whenever the box already terminates HTTPS for other services, because a second thing fighting over port 443 is a problem you create for yourself. If this is your first self-hosted application, All-in-One is a kinder starting point and you can migrate later.
services:
nextcloud-db:
image: postgres:16
container_name: nextcloud-db
restart: unless-stopped
environment:
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: ${NC_DB_PASSWORD}
volumes:
- nc-db:/var/lib/postgresql/data
nextcloud-cache:
image: redis:7-alpine
container_name: nextcloud-cache
restart: unless-stopped
nextcloud:
image: nextcloud:35-apache
container_name: nextcloud
restart: unless-stopped
depends_on:
- nextcloud-db
- nextcloud-cache
environment:
POSTGRES_HOST: nextcloud-db
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: ${NC_DB_PASSWORD}
REDIS_HOST: nextcloud-cache
NEXTCLOUD_TRUSTED_DOMAINS: cloud.example.com
TRUSTED_PROXIES: 192.168.1.50 # your reverse proxy's address as this container sees it
OVERWRITEPROTOCOL: https
PHP_MEMORY_LIMIT: 512M
PHP_UPLOAD_LIMIT: 4G
volumes:
- /srv/appdata/nextcloud/html:/var/www/html
- /srv/data/nextcloud:/var/www/html/data
volumes:
nc-db:
Check Nextcloud’s release schedule before copying the tag; each major version is supported for about a year. If you installed from an older copy of this guide that pinned nextcloud:30-apache, don’t jump straight to the current tag: step through each major version in turn (31, 32, 33, 34, 35), with a backup before each step.
database choice, briefly
Use PostgreSQL or MariaDB. Both are well supported and both will outlive your interest in this project. SQLite is offered and is fine for a single user kicking the tyres, but it falls over under concurrent sync clients, and migrating away from it later is an afternoon you will resent. Pick a real database on day one even if you are the only user.
Add Redis too. Nextcloud uses it for file locking and caching, and without it you will eventually meet the “file is locked” errors that make sync feel broken. It costs very little memory and removes a whole class of complaint.
HTTPS and the reverse proxy requirement

Nextcloud over plain HTTP is not a configuration, it is a mistake. Mobile apps expect TLS, some clients refuse to connect without it, and every password and file crosses your network in the clear. Put a reverse proxy in front — Caddy, Nginx or Traefik — terminate TLS there, and forward to the container.
You need a real hostname for a certificate, which means a domain pointing at your setup. If your connection has no public address, use a DNS provider that supports the challenge over DNS, or keep the whole thing reachable only over a private VPN and use an internal certificate. The proxy configuration follows the pattern in the Nginx web server setup guide, with two additions Nextcloud specifically needs: a generous client body size so large uploads are not rejected, and the forwarded-header and redirect rules that make service discovery work.
# Nginx fragments Nextcloud cares about
client_max_body_size 4G;
proxy_request_buffering off;
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
Then open Settings, Administration, Overview in Nextcloud itself. It runs a self-check and lists exactly which headers, redirects and limits it is unhappy about. Work that list down to empty before you invite anyone else onto the server.
phone photo auto-upload, the feature that justifies all of this

Install the Nextcloud app on the phone, sign in, then turn on auto-upload for the camera folder and for WhatsApp images if you want those too. Set it to upload only on Wi-Fi unless you enjoy paying for mobile data twice, and choose a target folder structured by date so the library stays browsable.
Two settings deserve thought. “Delete after upload” sounds tidy and I would leave it off, because a sync that misbehaves should not be able to remove the original. And on Android, battery optimisation will happily kill the app and stop your uploads for days; exclude Nextcloud from optimisation on every phone you set up. This one fails silently, so open the app on each handset once a month and check the last upload time rather than assuming.
Once photos land on the server, they are ordinary files in an ordinary folder, which means other things can use them. Point a media server at the same tree and family footage recorded on a phone shows up as an album on the living room TV without anybody copying anything.
external storage, for the drive you already have

You do not have to copy your existing files into Nextcloud’s data directory. The External Storage app mounts a local folder, an SMB share or S3-compatible object storage as a folder inside Nextcloud, leaving the files where they are. This is the tidy way to expose an existing media drive without duplicating a single byte.
Worth knowing before you rely on it: files changed outside Nextcloud are not noticed until something scans them, so add a periodic files:scan if other programs write there. Permissions have to line up with the web server’s user, or the mount appears empty with no explanation. And Nextcloud’s versioning and trash bin do not apply to external mounts the way they do to internal storage, so a deletion there is a real deletion.
# Useful occ commands, run inside the container as the web user
docker exec -u www-data nextcloud php occ status
docker exec -u www-data nextcloud php occ files:scan --all
docker exec -u www-data nextcloud php occ db:add-missing-indices
docker exec -u www-data nextcloud php occ maintenance:mode --on
performance basics that fix most complaints


A sluggish Nextcloud is usually not a hardware problem. Four settings account for most of it.
- PHP memory limit. The default is too small for photo previews and large uploads. Raising it to 512M is a reasonable starting point.
- Upload limits. PHP’s own upload size and your proxy’s body size both have to allow the largest file you will ever send. Whichever is smallest wins, silently.
- Memory caching. With Redis configured for locking and APCu for local cache, the interface stops feeling laggy on every page load.
- Preview generation. Generating thumbnails on demand for a folder of thousands of photos is painful. Pre-generate them on a schedule instead.
background jobs: move off Ajax immediately
Out of the box, Nextcloud runs its maintenance tasks when someone loads a page. That means they run erratically, or not at all if nobody visits for a week, and then a mysterious backlog appears. Switch the background job mode to Cron in the admin settings and give it a real schedule:
# Every five minutes, as root's crontab on the Docker host
*/5 * * * * /usr/bin/docker exec -u www-data nextcloud php -f /var/www/html/cron.php >/dev/null 2>&1
# Switch background jobs to Cron mode (same as the admin setting), then confirm the "last job ran" time on the Overview page
docker exec -u www-data nextcloud php occ background:cron
Then verify the next morning. A scheduled job you never confirmed is a job that might not exist, and Nextcloud’s overview page will tell you plainly when cron last ran.
backing up both halves, or you have backed up nothing

This is the part I would put in bold if the house style allowed it. Nextcloud is two things: files on disk and a database that describes them. A copy of the files without the database restores to a server that does not know those files exist. A copy of the database without the files restores to a catalogue of nothing. You need both, taken close together in time.
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%F)
OUT=/srv/backup/nextcloud
mkdir -p "$OUT"
docker exec -u www-data nextcloud php occ maintenance:mode --on
# Leave maintenance mode on exit, even if a step below fails
trap 'docker exec -u www-data nextcloud php occ maintenance:mode --off' EXIT
docker exec nextcloud-db pg_dump -U nextcloud nextcloud > "$OUT/db-$STAMP.sql"
tar -czf "$OUT/config-$STAMP.tar.gz" /srv/appdata/nextcloud/html/config
# Run the rsync snapshot of /srv/data/nextcloud here, inside the same window
echo "dumped database and config for $STAMP"
The data directory itself is far too large for a nightly tar, so it goes through incremental snapshots instead, using the script in my rsync backup guide. Call that snapshot from the marked line so it runs inside the same maintenance window, and the database and the files describe the same moment. The trap line switches maintenance mode back off whenever the script exits, including when a step fails, so a failed dump can’t leave the server locked for everyone.
Restore once, deliberately, into a scratch container. Until you have done that you have a backup procedure, not a backup.
common mistakes

- Backing up the files but not the database. Probably the most common self-hosting regret there is, and it only surfaces during a restore.
- Leaving background jobs on Ajax. Previews, cleanups and notifications then run whenever, which mostly means never.
- Choosing SQLite because the wizard offered it, then migrating under pressure when two laptops sync at once.
- Skipping two-factor authentication on the admin account while the service is reachable from the internet.
- Jumping multiple major versions at once. Upgrade one release at a time, with a snapshot you can roll back to before each step.
- Never hardening the host underneath. The application is only as private as the machine it sits on; my Ubuntu server security checklist is the baseline I apply before anything faces outward.
FAQ
is Nextcloud too heavy for a Raspberry Pi?
It runs, and for one or two users with modest libraries it is usable. The Pi’s weak points are preview generation for large photo collections and slow storage over USB. A Pi 5 with a decent SSD is a reasonable small deployment. For four people syncing laptops and phones, more memory and real SATA will make everyone happier.
can I skip the reverse proxy on my home network?
You can technically run it on plain HTTP inside your LAN, and some mobile clients will resist you the whole way. I would not. Even inside the flat, credentials crossing the network unencrypted is a habit worth not forming, and a proxy with a certificate is an hour of work once. If you truly never want it exposed, combine an internal certificate with VPN-only access.
what happens if my server dies with the only copy of my photos?
Then you have lost them, and this is why the backup section is not optional padding. Self-hosting removes someone else’s redundancy and asks you to supply your own. Minimum viable arrangement: the server, a local backup drive, and one copy that lives somewhere other than your flat. A drive at a relative’s house that you swap on visits counts perfectly.
how do I reach it from outside without exposing it?
Use a mesh VPN rather than a forwarded port; home lab networking: static IPs and VLANs explains why, and covers giving the server a stable local address.
Get auto-upload working on one phone this weekend and leave the rest of the apps alone until that has run for a month. The goal is not a self-hosted everything, it is never again having a month and a half of photographs living on one handset.
Update history: replaced the end-of-life Nextcloud 30 image with 35 and added an upgrade-path note, named the containers so the occ, cron and backup commands work, added reverse-proxy settings, made the backup script always leave maintenance mode, and updated Sources.
Sources
- Nextcloud administration manual — installation options, background jobs, caching, external storage and backup guidance.
- Nextcloud system requirements — supported databases, PHP versions and web server expectations.
- Ubuntu Server security how-to — hardening the host underneath Nextcloud.
- Nextcloud maintenance and release schedule — which major versions still receive security fixes.
- Nextcloud reverse proxy configuration — trusted proxies and overwrite settings.
- Nextcloud Docker image — environment variables used in the compose file above.