New releases
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Editorial pickCloud storage
Nextcloud is cloud storage on your own server: files sync with a computer and a phone, folders open to colleagues by link or by name, and a calendar, contacts, and notes sit on top. The recipe brings up the server, PostgreSQL, Valkey, and a separate container for background jobs.
Nextcloud is cloud storage that lives on your own server. Files sync with a computer and a phone, folders open to colleagues by name or through a public link, and on top of the files sits the familiar set: calendar, contacts, notes, tasks, and collaborative document editing. It fits a family, a small team, or anyone leaving Google Drive and Microsoft 365.
What separates it from plain file synchronisation is permissions and collaboration. There are accounts, groups, quotas, link expiry dates, passwords on public folders, and an activity log. Nextcloud is not “a folder on several devices” but a server with users.
The recipe brings up four containers: Nextcloud itself, PostgreSQL, Valkey for
file locking and caching, and a separate container for background jobs. Without
the last one Nextcloud falls back to AJAX mode and only runs maintenance while
someone has the interface open. The code, apps, and config.php live in a
Docker volume; user files live in a host directory.
The reliable level is declared partial: the database is a separate service and
there are healthchecks and a background worker, but the recipe sets no resource
limits and adds no external monitoring.
Nextcloud takes more maintenance than the other apps in this catalogue. Upgrades go one major version at a time, and after each one it is worth checking the security warnings page.
The containers run without privileged, host networking, or Docker socket
access, and use no-new-privileges; only the web port is published, and only on
127.0.0.1. PostgreSQL and Valkey are reachable only inside the Compose
network.
The administrator and database passwords are set in .env and end up in the
container environment: keep the file at mode 600, out of Git, and replace the
example values before the first start. After installation, enable two-factor
authentication — Nextcloud holds the files, calendars, and contacts of several
people at once.
The app store deserves particular care: add-ons execute code on the server with Nextcloud’s privileges, so install only the ones you trust. The built-in “Security & setup warnings” page shows what your particular installation is missing; after publishing over HTTPS it is worth walking the list and closing the warnings.
The main image is scanned with Trivy; the critical CVEs it found are listed on the card — mostly issues in the Debian system libraries the image is built on, which upstream closes with the next release. PostgreSQL and Valkey are pinned separately from the Nextcloud version and are outside the automated check.
These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Result of a dedicated Docker image scan, not a Compose configuration check.
A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.
Upstream Compose changes relative to the verified recipe.
Pinned image tags checked for newer versions.
From observation to action
The recipe already pins the latest known release. The steps below show the evidence retained for that version.
v34.0.3v34.0.3Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.
The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.
No image-tag change is currently planned.
Migration, majors cannot be skipped. Release-specific migration steps still come from the release notes and installation guide.
No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.
An exact Fully verified check for this target was recorded on 2026-08-31.
The status shows how fully the published recipe meets each level's requirements.
A minimal setup for a local network or access through a VPN.
The recipe covers every requirement at this level.
A public deployment with protected access and backups.
The recipe covers every requirement at this level.
A setup designed for predictable operations and observability.
You still need to add: external monitoring, cpu and memory limits.
Tailored to your server
Answer five questions and download a ready-to-run Nextcloud bundle.
Screenshots have not been added yet.
Read the full guide first: it explains external access, backups, and data locations.
cp .env.example .envdocker compose up -ddocker compose psFor this recipe budget at least 2 CPU cores, 2 GB of RAM, and 20 GB of disk for the server itself; 4 GB of RAM is recommended. This is a conservative estimate for the recipe: upstream publishes requirements for PHP and the database but not for the machine as a whole, and usage depends on the number of users and the enabled apps. Count storage for the files separately. You need Ubuntu 22.04+ or Debian 12+ with Docker Engine and Docker Compose v2.24+.
docker --version
docker compose version
nproc && free -m && df -h /srv
mkdir -p ~/services/nextcloud
cd ~/services/nextcloud
cp .env.example .env
chmod 600 .env
sudo mkdir -p /srv/nextcloud/data
sudo chown -R 33:33 /srv/nextcloud/data
sed -i "s|^NEXTCLOUD_DATA_LOCATION=.*|NEXTCLOUD_DATA_LOCATION=/srv/nextcloud/data|" .env
Inside the container Nextcloud runs as www-data with UID 33, so the data
directory has to belong to that user.
Always replace both example passwords:
sed -i "s|^NEXTCLOUD_DB_PASSWORD=.*|NEXTCLOUD_DB_PASSWORD=$(openssl rand -hex 24)|" .env
sed -i "s|^NEXTCLOUD_ADMIN_PASSWORD=.*|NEXTCLOUD_ADMIN_PASSWORD=$(openssl rand -base64 24)|" .env
NEXTCLOUD_VERSION pins the image; NEXTCLOUD_PORT sets the local port;
NEXTCLOUD_DATA_LOCATION is the directory with user files;
NEXTCLOUD_HTML_VOLUME and NEXTCLOUD_DB_VOLUME name the volumes for the code
and the database; NEXTCLOUD_DB_* and NEXTCLOUD_ADMIN_* configure database
access and the first account; NEXTCLOUD_TRUSTED_DOMAINS lists the domains
allowed to serve the instance; NEXTCLOUD_TRUSTED_PROXIES and
NEXTCLOUD_OVERWRITE_* are needed behind a reverse proxy; NEXTCLOUD_PHP_* set
the PHP limits; TZ sets the time zone.
The database password and user can only be changed before the first start: once PostgreSQL is initialised, the variable will not create a new account.
docker compose pull
docker compose up -d
docker compose ps
ssh -L 8080:127.0.0.1:8080 user@server.example
The first start takes longer than usual: the container unpacks the code and runs
the installation. The administrator is created automatically from the variables,
so there is no setup wizard in the browser — open http://localhost:8080 and log
straight in.
Check that background jobs run in their own container rather than through the browser:
docker compose exec -u www-data app php occ config:app:get core backgroundjobs_mode
The answer must be cron.
On a VPS keep the 127.0.0.1 bind, block port 8080 from outside, and publish the
service only through an HTTPS reverse proxy. Check the stack:
docker compose up -d
docker compose ps
curl --fail http://127.0.0.1:8080/status.php
All four containers must be up, and app, database, and redis must be
healthy. Keep in mind that preview generation and background jobs load a small
VPS noticeably: on a single shared core it is worth disabling preview generation
for large files.
Without TLS use an SSH tunnel or a VPN. If the reverse proxy runs on another host
in a trusted LAN, replace 127.0.0.1 in compose.yaml with the server’s LAN
address, add that address to NEXTCLOUD_TRUSTED_DOMAINS, and restrict the port
to the proxy address in the firewall.
Nextcloud refuses a request from a domain that is not in
NEXTCLOUD_TRUSTED_DOMAINS: instead of the interface you get an untrusted domain
message.
Replace cloud.example.com with your single domain in the file you pick from
proxy/, and put it in .env:
NEXTCLOUD_TRUSTED_DOMAINS=cloud.example.com
NEXTCLOUD_TRUSTED_PROXIES=172.16.0.0/12
NEXTCLOUD_OVERWRITE_PROTOCOL=https
NEXTCLOUD_OVERWRITE_CLI_URL=https://cloud.example.com
proxy/Caddyfile obtains a certificate automatically; proxy/nginx.conf
expects a Certbot certificate; proxy/traefik.yaml uses the letsencrypt
resolver. For Traefik in a container, replace 127.0.0.1 with a host gateway it
can reach.
All three samples do two things without which Nextcloud misbehaves: they lift the
upload size limit and redirect /.well-known/carddav and /.well-known/caldav
to /remote.php/dav — otherwise calendar and contacts do not connect in
clients.
docker compose up -d
curl --fail https://cloud.example.com/status.php
chmod +x backup.sh restore.sh
./backup.sh
The script enables maintenance mode, takes a PostgreSQL dump, archives the volume
with the code, apps, and config.php, then turns maintenance mode off. Without
maintenance mode the database and file copies can drift apart.
User files are not in the archive: their directory can grow to terabytes and is better copied incrementally:
rsync -a --delete /srv/nextcloud/data/ /mnt/backup/nextcloud-data/
A full restore needs three parts: the database dump, the code volume, and the file directory. Keep them consistent, and keep at least one copy off the server.
One detail that trips people up when backing up by hand: the Nextcloud installer
creates a separate PostgreSQL role named oc_<admin name> for normal operation,
and the tables belong to it rather than to the user from .env. That is why
backup.sh dumps the role list alongside the database — without it the dump will
not load into a clean PostgreSQL.
Restoring replaces the database and the code completely:
./restore.sh ./backups/nextcloud-YYYYMMDDTHHMMSSZ.tar
docker compose ps
curl --fail http://127.0.0.1:8080/status.php
Before replacing anything the script takes a safety copy of the current state, removes the database volume, and loads the dump into a clean database. The user file directory is untouched: if it was lost, restore it from your own copy before starting, otherwise Nextcloud shows entries without files.
After a restore it is worth rescanning the files:
docker compose exec -u www-data app php occ files:scan --all
Take a backup and read the release notes. Upgrade strictly one major version at a time: you cannot jump from 32 to 34, the installer will stop the transition.
./backup.sh
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 app
docker compose exec -u www-data app php occ status
After the upgrade, look at Administration → Overview: new recommendations and warnings appear there when something is configured wrong.
Nextcloud database migrations are irreversible: an older image will not start
against a database a newer version has upgraded. Restore the pinned
NEXTCLOUD_VERSION in .env and restore the archive taken before the update:
docker compose pull
./restore.sh ./backups/nextcloud-before-update.tar
docker compose up -d
Files uploaded after the update stay on disk, but the restored database has no
records of them: bring them back with occ files:scan --all.
docker compose down removes the containers but keeps the database, the code,
and the files. Complete, irreversible removal after verifying a backup:
docker compose down
docker volume rm nextcloud-html nextcloud-database
sudo rm -rf /srv/nextcloud/data
rm -rf ~/services/nextcloud
Sources: Docker installation, administration manual, reverse proxy, background jobs, caching and locking, and backup.
Nextcloud only accepts requests from domains on its list. Add yours to .env and
restart:
grep NEXTCLOUD_TRUSTED_DOMAINS .env
docker compose up -d
docker compose exec -u www-data app php occ config:system:get trusted_domains
The variable is applied at container start; to change the list on a running
instance use
occ config:system:set trusted_domains 1 --value=cloud.example.com.
Behind a reverse proxy Nextcloud does not know the outside is HTTPS. Fill in
NEXTCLOUD_TRUSTED_PROXIES, NEXTCLOUD_OVERWRITE_PROTOCOL=https, and
NEXTCLOUD_OVERWRITE_CLI_URL in .env, then restart the stack.
Clients look for /.well-known/caldav and /.well-known/carddav. Those
addresses have to redirect to /remote.php/dav — the samples in proxy/ already
carry the rules. Check with:
curl -sI https://cloud.example.com/.well-known/caldav | head -3
docker compose ps cron
docker compose exec -u www-data app php occ config:app:get core backgroundjobs_mode
docker compose logs --tail=50 cron
The mode must be cron. The value is written the first time cron.php runs, so
right after installation the field can be empty — the cron container executes
the job every five minutes.
The limit usually comes from the reverse proxy rather than PHP. Nginx needs
client_max_body_size, Caddy needs request_body max_size, and Traefik needs
the buffering middleware; the samples in proxy/ are already configured. On
the PHP side the size comes from NEXTCLOUD_PHP_UPLOAD_LIMIT.
Inside the container Nextcloud runs as UID 33:
ls -ld /srv/nextcloud/data
sudo chown -R 33:33 /srv/nextcloud/data
Nextcloud keeps the file list in the database and does not scan the directory on its own. After copying files by hand, run:
docker compose exec -u www-data app php occ files:scan --all
That happens when an upgrade was interrupted:
docker compose exec -u www-data app php occ maintenance:mode --off
docker compose exec -u www-data app php occ status
If the server complains about an unfinished upgrade, run occ upgrade first, and
restore from an archive if that fails.
That is a normal state right after installation: some checks concern HTTPS and headers that only appear once the instance is published through a reverse proxy. Walk the list under Administration → Overview and close the items one by one — each links to the relevant documentation.
docker compose logs --tail=200 app | grep -i "error\|upgrade\|migration"
You cannot skip a major version: upgrade one at a time. Downgrades are not supported — the only way back is restoring the archive taken before the update.
Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.
A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.