Nextcloud
Nextcloud: files, sync and collaboration on your own server
Nextcloud on Cubeship
Nextcloud is the open-source file sync and share platform: your files, calendars and contacts on your own server, reached from the browser, the desktop and mobile clients, and WebDAV.
This template installs it on a Cubeship instance with the managed PostgreSQL and Redis it needs, a volume for everything it keeps in files, and its background jobs running every 5 minutes.
What it creates
- nextcloud — Nextcloud
34.0.4, built on the instance from theDockerfilein this repository, answering on the domain you choose, with a volume at/var/www/html: Nextcloud itself,config/config.php, installed apps, anddata/— every user's files. - nextcloud-db — a managed PostgreSQL 18 database, attached to the app. Users, shares, calendars, contacts and the file index are in it.
- nextcloud-redis — a managed Redis 7.4, attached to the app. Nextcloud's file locks, its distributed cache and PHP sessions are in it.
It needs Cubeship 0.7.0 or newer, and an admin to install it: the app is built on the instance, and only admins build.
Why it is built
Nextcloud runs background jobs — cleaning up trash and versions, sending
notifications, scanning external storage — from cron.php, and recommends
system cron every 5 minutes. The published image, nextcloud:34.0.4-apache,
runs Apache and nothing else; with docker compose the image's /cron.sh runs
in a second container sharing the same volume. Cubeship has no sidecars and no
cron, and a volume belongs to one app, so that second container cannot exist.
The Dockerfile here is instead the image's own example for this case,
.examples/dockerfiles/cron in
nextcloud/docker: the same image with
supervisord running Apache and /cron.sh side by side, and
NEXTCLOUD_UPDATE=1, without which the entrypoint installs nothing unless the
command is Apache. The first time cron runs cron.php, Nextcloud switches its
background jobs to Cron by itself.
The alternatives Nextcloud offers are worse here: AJAX runs one job per page view and its documentation calls it the least reliable; Webcron runs one job per call from an outside service, which it calls fit only for very small instances.
What you are asked
| Input | What to give |
|---|---|
| Where Nextcloud answers | A domain you control, pointed at your instance. |
| The admin's username | Anything; admin unless you change it. |
| The admin's password | Nothing — the instance generates it and shows it once. Keep a copy. |
Nextcloud installs itself on the first start, with that account, before Apache answers, so there is no install wizard for a stranger to reach first.
After installing
- Wait for the first start to finish: it copies Nextcloud into the volume and creates the database tables, which takes a minute or two. The domain answers once it is done.
- Open the domain and sign in with the admin's username and the generated password.
- Open Administration settings → Overview. It lists what is left, usually:
- Email server — set one under Administration settings → Basic settings. Until you do, password resets and share notifications go nowhere.
- Default phone region and maintenance window — set both with
occ, below.
Running occ
Cubeship has no console into an app, so occ runs over SSH on the machine the
app is on, as www-data:
docker exec -u www-data $(docker ps -qf name=cubeship-nextcloud-production-nextcloud) \
php occ status
The name follows the project, environment and app names you install it with. The two commands the overview usually asks for:
php occ config:system:set default_phone_region --value=US
php occ config:system:set maintenance_window_start --type=integer --value=1
Behind Cubeship's proxy
TLS ends at Cubeship's proxy, and Nextcloud gets plain HTTP. The app's
variables pin every address it builds to https://<your domain>:
OVERWRITEPROTOCOL, OVERWRITEHOST and OVERWRITECLIURL.
TRUSTED_PROXIES is the three private ranges Docker takes addresses from,
because Cubeship does not fix the subnet of the network the proxy reaches the
app over. It is what lets Nextcloud read the visitor's address from
X-Forwarded-For. The cost: any other container on the instance could claim a
different address in that header, which affects only Nextcloud's logs and its
brute-force throttling. The image's Apache already trusts the same ranges for
X-Real-IP.
NEXTCLOUD_TRUSTED_DOMAINS is read only on the first start. If the domain
changes, change OVERWRITEHOST and OVERWRITECLIURL on the app, then add
the new name with occ before redeploying, or Nextcloud refuses it:
php occ config:system:set trusted_domains 2 --value=cloud.example.org
The health check
The health check asks for /core/img/favicon.ico, a file Apache serves
without Nextcloud. The proxy's probe does not carry your domain as its host,
and Nextcloud answers 400 to every host that is not trusted —
/status.php included.
Uploads
PHP accepts up to 512 MB per request (PHP_UPLOAD_LIMIT) and a script may use
512 MB of memory (PHP_MEMORY_LIMIT). The web interface and the desktop and
mobile clients upload large files in chunks, so the limit rarely matters; set
either variable on the app and redeploy to change it.
Updating Nextcloud
Nextcloud's web updater is disabled in this image. Change the tag in the
Dockerfile, release this repository, and point ref at the new release. On
the next start the entrypoint copies the new version into the volume and runs
occ upgrade.
Nextcloud upgrades one major version at a time: from 34 to 35, then to 36, never 34 to 36 — the entrypoint refuses to start otherwise. Back up the volume and the database first.
The database password
Nextcloud's installer, given a PostgreSQL user that may create roles, creates
its own user, oc_admin, with a password it generates, and writes both into
config/config.php in the volume. Nextcloud connects as that user from then
on, so the database's own credentials no longer need to match what the app was
installed with.
The volume
The app runs as one copy on the machine its volume is on, and a deploy stops it for a few seconds. Back up both the volume, for the files and the configuration, and the database, for everything that indexes them: one without the other does not restore.
Every user's files are in the volume. Size the disk of the machine the app is on for them.
Resources
The app is limited to 2 CPUs and 2 GiB of memory, which is Apache, PHP and
cron together. Raise limits in template.yaml for many users, previews of
large images, or apps like Nextcloud Office.
What this creates
nextcloud
https://github.com/cubeshipd/cubeship-nextcloud-template
nextcloud-db
Postgres 18
nextcloud-redis
Redis 7.4
/var/www/html
Volume of nextcloud
# yaml-language-server: $schema=https://cubeship.dev/schema/template/v1.json
version: 1
# The first release that keeps a volume's data across deploys.
minCubeship: "0.7.0"
project: nextcloud
inputs:
- key: domain
type: domain
label: Where Nextcloud answers
- key: adminUser
type: text
label: The admin's username
default: admin
pattern: ^[A-Za-z0-9_.@-]+$
- key: adminPassword
type: secret
label: The admin's password
generate: 32
databases:
- key: db
name: nextcloud-db
# Nextcloud recommends PostgreSQL 18. Its other recommendation, MariaDB,
# also needs READ COMMITTED and row-based binary logging on the server.
engine: postgres
version: "18"
database: nextcloud
- key: cache
name: nextcloud-redis
engine: redis
version: "7.4"
apps:
- key: web
name: nextcloud
# The published image runs Apache alone, and background jobs need cron.
# The Dockerfile in this repository is that image running Apache and cron
# under supervisord.
repo: https://github.com/cubeshipd/cubeship-nextcloud-template
ref: v1.0.0
build: dockerfile
port: 80
# A file Apache serves without Nextcloud. status.php and every page answer
# 400 to a Host that is not a trusted domain, and the probe's is not.
health: /core/img/favicon.ico
domains:
- host: ${input.domain}
attach:
- database: db
# Writes the REDIS_HOST the image reads; its port and password are below.
- database: cache
volumes:
- path: /var/www/html
limits: { cpu: 2, memory: 2Gi }
env:
# Read on the first start, which installs Nextcloud before Apache answers.
POSTGRES_HOST: ${db.db.host}:${db.db.port}
POSTGRES_DB: ${db.db.name}
POSTGRES_USER: ${db.db.user}
POSTGRES_PASSWORD: ${db.db.password}
NEXTCLOUD_ADMIN_USER: ${input.adminUser}
NEXTCLOUD_ADMIN_PASSWORD: ${input.adminPassword}
NEXTCLOUD_TRUSTED_DOMAINS: ${input.domain}
REDIS_HOST_PORT: ${db.cache.port}
REDIS_HOST_PASSWORD: ${db.cache.password}
OVERWRITEHOST: ${input.domain}
OVERWRITEPROTOCOL: https
OVERWRITECLIURL: https://${input.domain}
# Docker's default address pools, bridge and overlay: the proxy reaches
# the app from one of them, and Cubeship does not pin which.
TRUSTED_PROXIES: 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16