What is Nextcloud?
Nextcloud is a self-hosted content collaboration platform for file sync and sharing, calendars, contacts, notes and video calls. It is open source under the AGPL-3.0 license and has desktop sync clients for Windows, macOS and Linux plus mobile apps for iOS and Android. Its app store adds hundreds of extensions, from office suites to password managers.
Requirements
- A Linux server with 2 vCPU and 4 GB of RAM for a household or small team.
- Docker Engine and Docker Compose v2.
- Enough disk for your files. Put the data volume on the largest disk.
- A domain name. Nextcloud clients expect a stable HTTPS URL.
Step 1: Prepare the server
This guide assumes Ubuntu 24.04 with Docker installed. If needed, follow the Docker Engine install guide. Create a project folder:
mkdir -p ~/nextcloud && cd ~/nextcloud
Nextcloud also offers All-in-One, which is the officially recommended route and manages everything for you. This guide uses the community-maintained nextcloud image instead, which keeps the stack as a normal Compose file.
Step 2: Create the Docker Compose file
The stack has four containers: Postgres for the database, Redis for file locking and caching, the Nextcloud app, and a cron container that runs background jobs every five minutes. Save as docker-compose.yml:
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- db:/var/lib/postgresql/data
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
app:
image: nextcloud:35-apache
restart: unless-stopped
ports:
- "127.0.0.1:8080:80"
depends_on:
- db
- redis
environment:
POSTGRES_HOST: db
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
REDIS_HOST: redis
REDIS_HOST_PASSWORD: ${REDIS_PASSWORD}
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
NEXTCLOUD_TRUSTED_DOMAINS: cloud.example.com
OVERWRITEPROTOCOL: https
OVERWRITECLIURL: https://cloud.example.com
TRUSTED_PROXIES: 172.16.0.0/12
PHP_MEMORY_LIMIT: 1G
PHP_UPLOAD_LIMIT: 16G
volumes:
- nextcloud:/var/www/html
cron:
image: nextcloud:35-apache
restart: unless-stopped
entrypoint: /cron.sh
depends_on:
- db
- redis
volumes:
- nextcloud:/var/www/html
volumes:
db:
nextcloud:
Create .env next to it:
POSTGRES_PASSWORD=CHANGE_ME
REDIS_PASSWORD=CHANGE_ME
ADMIN_PASSWORD=CHANGE_ME
Generate each value with openssl rand -hex 32. Save the admin password in your password manager. The NEXTCLOUD_ADMIN_* variables and database settings are only read on the very first start, when Nextcloud installs itself. After that, configuration lives in config/config.php inside the volume.
The 35-apache tag follows the latest 35.x release, so patch updates arrive with docker compose pull, while major upgrades stay under your control.
Step 3: Start and open the app
docker compose up -d
docker compose logs -f app
The first start copies Nextcloud into the volume and runs the installer, which takes a minute or two. When the log shows Apache running, finish Step 4 and open https://cloud.example.com. Sign in as admin with the password from .env. Go to Administration settings, Basic settings, and confirm background jobs are set to "Cron". Then visit Overview, where Nextcloud runs its security and setup checks.
Step 4: Put it behind HTTPS
With Caddy on the host:
cloud.example.com {
request_body {
max_size 16GB
}
redir /.well-known/carddav /remote.php/dav/ 301
redir /.well-known/caldav /remote.php/dav/ 301
reverse_proxy 127.0.0.1:8080
}
The .well-known redirects let calendar and contacts apps discover the server. TRUSTED_PROXIES covers Docker's default bridge networks so Nextcloud sees real client IPs. Add the Strict-Transport-Security header once you are sure HTTPS works.
Backups and upgrades
Back up three things: the database (docker compose exec db pg_dump -U nextcloud nextcloud > nextcloud.sql), the nextcloud volume (which holds config/ and data/), and your .env. Put Nextcloud in maintenance mode during the backup for consistency:
docker compose exec -u www-data app php occ maintenance:mode --on
# run your backup
docker compose exec -u www-data app php occ maintenance:mode --off
For patch updates, run docker compose pull && docker compose up -d. For a major upgrade, change the image tag by exactly one version (for example 35-apache to 36-apache) in both services, then pull and start. The container runs the upgrade automatically.
Troubleshooting
- "Access through untrusted domain": add your domain with
docker compose exec -u www-data app php occ config:system:set trusted_domains 1 --value=cloud.example.com. - Login loops or wrong redirect URLs:
OVERWRITEPROTOCOLmust behttpsbehind a TLS proxy. Set it inconfig.phpif the volume already existed. - Large uploads fail: raise
PHP_UPLOAD_LIMITand the proxy body size limit together. - Warnings about missing indices after an upgrade: run
docker compose exec -u www-data app php occ db:add-missing-indices.
Next steps
Install the desktop and mobile sync clients, enable two-factor authentication, add Nextcloud Office or Collabora for document editing, and configure an SMTP server for notifications.
Spotted something out of date? Tell us and we will update the guide.