Docker image
Nextcloud in Docker. Redis, APCu, proxy-aware.
Nextcloud is an open source platform for file sync and share, calendar, contacts, notes, and real-time collaboration that you run on your own infrastructure. It gives a team a private alternative to hosted file and office suites, with desktop and mobile clients, document editing, and a large app ecosystem.
Prefer not to run it yourself? Managed Nextcloud from $6.67/mo
# Nextcloud in Docker, base pinned by digest docker pull ghcr.io/remarkablecloud/nextcloud:34.0.3-r1 docker compose up -d
- Current build
34.0.3-r1, updated Sep 10, 2026- Base
- Nextcloud stable (Apache), digest-pinned
- What's inside
- Redis file locking and cache, APCu, MariaDB pairing, trusted-domain wiring
- Upstream license
- Nextcloud: AGPL-3.0-or-later
The image
What our image adds.
What changes from upstream Nextcloud, and why it matters in production.
Digest-pinned base
The image is built from nextcloud:stable-apache@sha256:b97df9e0e1ee… (Nextcloud 34.0.3). Pinning by digest keeps rebuilds reproducible: the base changes only when we bump the digest on purpose.
Reverse-proxy config baked in
The image ships overwriteprotocol=https, a trusted_proxies list for the private container networks, and overwritehost plus overwrite.cli.url derived from NC_OVERWRITE_HOST. The hostname is read from the environment on each request, so canonical links always point at the right host.
Redis and APCu caching wired
Redis is used for the distributed cache and for file locking, and APCu is enabled for the fast local cache, which is the configuration Nextcloud recommends for a responsive multi-user instance.
PHP tuned
OPcache is enabled and sized, and expose_php is turned off so the PHP version banner is not advertised.
Automatic first-run admin password
On the first install, if no admin password is provided, the image generates a strong random password and prints it once to the container log to be handed to the customer. No default credential is ever baked in.
Healthcheck
A container HEALTHCHECK polls /status.php so the platform routes traffic only once Nextcloud is up and its code is in place. The current published tag is ghcr.io/remarkablecloud/nextcloud:34.0.3-r1.
Run it yourself, or not
Same image either way.
Pull it and run it anywhere, or let us run Nextcloud for you on a managed server with updates, monitoring and snapshots.
Run Nextcloud yourself
- Pull it from GHCR and run it on any Docker host
- The guide below: compose, environment, backups, upgrades
- You patch, back up and watch it
Nextcloud on a managed server
- Installed for you from this same image, $0 for the app
- Updates with a snapshot first, monitoring and a managed firewall
- A 2 GB server from $6.67/mo billed annually, with room for more apps
The guide
Run it in production.
Compose file, environment reference, hardening, backups and upgrades, written by the people who build the image.
On this page
Architecture at a glance
- Nextcloud (Apache and PHP) serves plain HTTP on port
80. TLS is terminated upstream by a reverse proxy (Traefik on the RemarkableCloud App Platform), so there is no in-container certificate to manage. - MariaDB is a separate database container and holds all metadata (users, shares, file index, app data).
- Redis is a separate container used for the distributed cache and transactional file locking.
- A data volume at
/var/www/htmlholds the Nextcloud code, the mergedconfig/, apps, and user files that must survive a container replacement.
Docker Compose walkthrough
The standalone stack (Nextcloud, MariaDB, Redis, and named volumes) is defined
in the image’s docker-compose.yml:
name: nextcloud
services:
db:
image: mariadb:11
restart: unless-stopped
command: ["--transaction-isolation=READ-COMMITTED", "--log-bin=binlog", "--binlog-format=ROW"]
environment:
MARIADB_DATABASE: nextcloud
MARIADB_USER: nextcloud
MARIADB_PASSWORD: ${DB_PASSWORD:-change-me-db}
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-change-me-root}
volumes: [db-data:/var/lib/mysql]
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 12
redis:
image: redis:7-alpine
restart: unless-stopped
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:-change-me-redis}"]
app:
image: ${NC_IMAGE:-ghcr.io/remarkablecloud/nextcloud:34.0.3-r1}
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports: ["${HTTP_PORT:-8082}:80"]
environment:
MYSQL_HOST: db
MYSQL_DATABASE: nextcloud
MYSQL_USER: nextcloud
MYSQL_PASSWORD: ${DB_PASSWORD:-change-me-db}
REDIS_HOST: redis
REDIS_HOST_PORT: "6379"
REDIS_HOST_PASSWORD: ${REDIS_PASSWORD:-change-me-redis}
NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER:-admin}
NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD:-}
NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_TRUSTED_DOMAINS:-localhost}
NC_OVERWRITE_HOST: ${NC_OVERWRITE_HOST:-}
volumes:
- nc-data:/var/www/html
volumes:
db-data:
nc-data:
Step by step:
- MariaDB is tuned for Nextcloud.
READ-COMMITTEDisolation with row-based binary logging is the configuration Nextcloud recommends. The healthcheck waits until InnoDB is initialized. - Redis requires a password. The
redisservice starts with--requirepass, and the app connects withREDIS_HOST_PASSWORD, so the cache and lock store are not open on the network. - The app waits for the database.
depends_onwithcondition: service_healthyavoids a first-boot race against MariaDB. - The image is pinned.
NC_IMAGElets you override the tag for a local trial. The App Platform does not use this compose file; it deploys the catalog tag directly (currently34.0.3-r1), which is also the compose default here. - The admin password is generated if blank.
NEXTCLOUD_ADMIN_PASSWORDis left empty here, so the image creates one on first install and logs it once. - Only the web port is exposed.
${HTTP_PORT:-8082}:80is for local use. In production Traefik connects to port80on the internal network and nothing is bound on the host. - State lives in volumes.
nc-data(/var/www/html) anddb-datapersist across restarts and upgrades.
To start it locally:
REDIS_PASSWORD=$(openssl rand -hex 16) DB_PASSWORD=$(openssl rand -hex 16) \
NEXTCLOUD_ADMIN_PASSWORD=$(openssl rand -hex 12) docker compose up -d
Then browse to http://localhost:8082. If you leave NEXTCLOUD_ADMIN_PASSWORD
unset, read the generated password from docker compose logs app.
For production, set NC_OVERWRITE_HOST and NEXTCLOUD_TRUSTED_DOMAINS to your
real domain, point the MYSQL_* and REDIS_* variables at your managed
services, and put the container behind a TLS-terminating proxy.
Environment variable reference
These are the values the App Platform backend wires automatically from the
managed MariaDB and Redis ({...} placeholders are filled per install). They
are listed here for standalone and self-hosted runs.
| Variable | Required | Value / default | Purpose |
|---|---|---|---|
MYSQL_HOST | yes | <db_host> | MariaDB host (the db service in Compose). |
MYSQL_DATABASE | yes | <db_name> | Database name. |
MYSQL_USER | yes | <db_user> | Database user. |
MYSQL_PASSWORD | yes | <db_password> | Database password. |
REDIS_HOST | yes | <redis_host> | Redis host for distributed cache and file locking. |
REDIS_HOST_PORT | yes | 6379 | Redis port. |
REDIS_HOST_PASSWORD | yes | <redis_password> | Redis password. |
NEXTCLOUD_ADMIN_USER | yes | admin | Admin account created on first install. |
NEXTCLOUD_ADMIN_PASSWORD | no | (generated) | Admin password. Leave empty to auto-generate on first run; the value is printed once to the container log. |
NEXTCLOUD_TRUSTED_DOMAINS | yes | <hostname> | Domains Nextcloud will answer for. |
NC_OVERWRITE_HOST | yes | <hostname> | Canonical host for links and CLI URLs behind the proxy. |
Only the first install reads NEXTCLOUD_ADMIN_USER and
NEXTCLOUD_ADMIN_PASSWORD; once config/config.php exists they are ignored, and
account changes happen inside Nextcloud.
Hardening notes
- No baked credentials. The admin password is generated at runtime on first install and printed once to the log, never stored in an image layer.
- Reverse-proxy trust is scoped.
trusted_proxiescovers the private container ranges only, andoverwriteprotocol=httpsensures Nextcloud builds HTTPS links behind the proxy without trusting arbitrary forwarded headers. - PHP version hidden.
expose_php=Offremoves theX-Powered-Bybanner. - Digest-pinned base. Each build is auditable and reproducible.
- Healthcheck gates traffic.
/status.phpmust return success before the platform routes requests. - Keep MariaDB and Redis private. Do not publish their ports to the host or the internet; Redis is password-protected in the standalone stack.
- After install, run the security checklist. In Nextcloud’s admin overview,
clear any warnings (for example, add any missing recommended database indices
with
occ). For the background job mode, note that this stack has no cron sidecar; the default is AJAX, and switching to the recommended Cron mode requires a separate cron runner, which is not included here.
Backups
Capture the database and the data volume together for a restorable backup:
- The MariaDB database:
docker compose exec db mysqldump -u root -p"$DB_ROOT_PASSWORD" nextcloud > nextcloud-db.sql - The data volume at
/var/www/html(config, apps, and user files):docker run --rm -v nextcloud_nc-data:/data -v "$PWD":/backup alpine \ tar czf /backup/nextcloud-data.tgz -C /data .
For a fully consistent copy of large instances, enable maintenance mode first
(occ maintenance:mode --on), back up, then turn it off. On the RemarkableCloud
App Platform, database and volume backups follow the platform’s backup schedule.
Upgrades
- Nextcloud upgrades one major version at a time. Do not skip a major release; move 34 to 35 to 36 in order, not straight to a later version.
- New builds ship as new
-rtags. Pull the new tag and recreate the container; the upstream entrypoint runs the upgrade and any migrations on start:docker compose pull app && docker compose up -d app - Always back up first. Keep the database dump and data archive from just before the upgrade.
- Read the changelog. Every build is listed in
CHANGELOG.mdwith the upstream version, base image digest, and any behavior or breaking changes. - Watch app compatibility. Third-party apps may need updates or may be temporarily disabled across a major upgrade.
Changelog
Each build is pinned by digest and recorded here, so you can see what changed before you pull a new tag.
-
34.0.3-r1Current Sep 10, 2026 First changelog entry for the RemarkableCloud Nextcloud image.
Nextcloud and other product names are trademarks of their respective owners. RemarkableCloud is not affiliated with or endorsed by them.
FAQ
Nextcloud in Docker, answered.
Short answers from the people who build and run this image.
Does this image include a database or Redis?
No. MariaDB and Redis run as separate containers or managed services. The standalone compose file includes both for convenience.
Can I use MySQL instead of MariaDB?
Yes, Nextcloud supports MySQL 8 as well; point the MYSQL_* variables at it. The RemarkableCloud stack ships MariaDB by default.
Where does the admin password come from?
If you set NEXTCLOUD_ADMIN_PASSWORD, that value is used. If you leave it empty, the image generates a strong random password on first install and prints it once to the container log.
How does HTTPS work if the container only serves HTTP on port 80?
TLS is terminated at the reverse proxy (Traefik on the App Platform). The image sets overwriteprotocol=https and trusts the private proxy network, so Nextcloud builds correct HTTPS links.
Why do I need Redis?
Redis provides the distributed cache and transactional file locking, which keeps a multi-user instance responsive and avoids file-locking errors. APCu handles the fast local cache.
Pull it, or let us run it.
The same image runs Nextcloud on our managed servers, updates and monitoring included.