Self-Host Nextcloud in 2026: The Complete Docker Compose Guide
Master self-hosting Nextcloud v34 with this step-by-step Docker Compose guide. Covers requirements, full configs, SSL, backups, and common pitfalls.
Introduction
Self-hosting your own cloud storage is the definitive step toward digital sovereignty. Nextcloud v34.0.3 (released 2026-08-13) is the leading open-source platform that lets you replace Google Drive, Dropbox, and even Microsoft 365 with a solution you fully control. This guide is not a high-level overview; it is a precise, production-oriented walkthrough.
You will learn how to deploy Nextcloud using Docker Compose with a PostgreSQL database and Redis cache. We will structure the deployment from a clean Ubuntu 24.04 LTS server, covering every file, command, and permission. We will not skip the hard parts: correct file ownership, environment variable management, and reverse proxy setup with TLS.
By the end, you will have a secure, fast, and upgradeable Nextcloud instance. You will also understand the architecture well enough to troubleshoot it without panic. This guide assumes you are comfortable with the Linux command line and basic Docker concepts. If you are new to Docker, you should first review the official Docker documentation for docker compose.
All commands are provided as complete, copy-paste-ready blocks. The only variables you must change are your domain name, timezone, and the secrets in your .env file. We will strictly adhere to the latest official Nextcloud v34.0.3 release to avoid version-specific pitfalls.
Prerequisites
Before you begin, you need a host machine that meets the following minimum specifications. These are not arbitrary numbers; they are the baseline for a smooth experience with a few users and typical workloads.
| Component | Minimum Requirement | Recommended | Notes |
|---|---|---|---|
| CPU | 2 cores | 4 cores | Nextcloud is PHP-based; more cores help with background jobs and multiple concurrent requests. |
| RAM | 4 GB | 8 GB | PostgreSQL and Redis benefit from RAM. The PHP-FPM process also consumes memory per request. Typical usage for 5-10 users is 2-4 GB, but this varies with file preview generation and user activity. |
| Storage | 100 GB free space | 1 TB+ | This is for your data. The Nextcloud installation itself is small (~1 GB), but your user files will grow. Use a separate mount point for your data directory if possible. |
| OS | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS | Any modern Linux distribution with Docker Engine 24+ and Docker Compose v2 will work. |
| Software | Docker Engine, Docker Compose v2 | Latest stable | Install from the official Docker repository, not the distro's default package. |
| Domain | A record pointing to your server IP | Subdomain like cloud.example.com |
Required for trusted domain configuration and Let's Encrypt SSL. |
| Network | Open ports 80 and 443 | - | Needed for the reverse proxy and SSL certificate issuance. |
Important: Before proceeding, verify your Docker installation is functional with docker run hello-world. Also, ensure you have git installed for the initial setup.
Step-by-Step Installation Guide
Step 1: Create Project Directory and .env File
First, we create a dedicated directory for the project and navigate into it. We will also create the crucial .env file that will hold all secrets and configuration variables. This file is the single source of truth for your configuration.
mkdir -p ~/nextcloud && cd ~/nextcloud && \
touch .env && \
chmod 600 .env
Now, edit the .env file with your favorite text editor (e.g., nano .env). Fill in your specific values. Never commit this file to a Git repository. It contains your database password and other secrets.
POSTGRES_PASSWORD=change_this_strong_password
POSTGRES_DB=nextcloud
POSTGRES_USER=nextcloud
REDIS_HOST=redis
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=change_this_admin_password
NEXTCLOUD_TRUSTED_DOMAINS=cloud.example.com
NEXTCLOUD_DATA_DIR=/var/www/html/data
TIMEZONE=UTC
Step 2: Create docker-compose.yml
In the same directory, create a file named docker-compose.yml. This file defines the three services: the database, the cache, and the main application. We use the official images and pin the Nextcloud version to ${NEXTCLOUD_VERSION:-v34.0.3}. This allows you to override it from your .env file by adding NEXTCLOUD_VERSION=latest if you wish, but we recommend staying on the pinned version for stability.
services:
db:
image: postgres:16-alpine
container_name: nextcloud-db
restart: unless-stopped
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: nextcloud-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
app:
image: nextcloud:${NEXTCLOUD_VERSION:-v34.0.3}
container_name: nextcloud-app
restart: unless-stopped
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "8080:80"
environment:
- POSTGRES_HOST=db
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- REDIS_HOST=redis
- NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER}
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
- NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_TRUSTED_DOMAINS}
- PHP_UPLOAD_LIMIT=10G
- PHP_MEMORY_LIMIT=512M
volumes:
- nextcloud_data:/var/www/html
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/status.php"]
interval: 30s
timeout: 10s
retries: 3
volumes:
db_data:
redis_data:
nextcloud_data:
Step 3: Deploy the Stack
Now we pull the images and start the containers. The first startup will take a few minutes as the database initializes and Nextcloud installs.
cd ~/nextcloud && \
docker compose up -d
Step 4: Verify the Installation
Check the status of the containers. Ensure all three are running and the health checks are passing.
docker compose ps
You should see healthy under the STATUS column for all services. Then, access your server's IP on port 8080 in a web browser: http://your_server_ip:8080. You should see the Nextcloud login page. Log in with the admin username and password from your .env file.
Step 5: Configure Nextcloud Data Directory (Optional but Recommended)
By default, your user files are stored inside the nextcloud_data volume. This is fine for testing, but for production, you likely want a dedicated directory on your host. To do this, modify the app service volume mount in your docker-compose.yml:
volumes:
- nextcloud_data:/var/www/html
- /path/to/your/nextcloud-data:/var/www/html/data
Replace /path/to/your/nextcloud-data with an absolute path on your host. Critical: The web server user inside the container must own this directory. The official Nextcloud image runs as www-data (UID 33). Run id -u www-data inside the container to verify. On your host, you must chown the data directory to that UID. Run id -u && id -g on your host and verify against the image's documentation (default user in this image is www-data).
sudo chown -R 33:33 /path/to/your/nextcloud-data
After this change, restart the stack.
cd ~/nextcloud && \
docker compose down && \
docker compose up -d
Step 6: Set Up Background Jobs (Cron)
Nextcloud relies on background jobs for tasks like file scanning and expiration. The default is AJAX, which is not reliable. We will switch to Cron. Add a cron job on your host that runs the cron.php script every 5 minutes.
crontab -e
Add the following line, adjusting the path if your stack is not in ~/nextcloud.
*/5 * * * * docker exec -u www-data nextcloud-app php -f /var/www/html/cron.php
Step 7: Configure Trusted Domains (If Not Using Reverse Proxy Yet)
If you are accessing Nextcloud by IP address, you must add it to the trusted domains list. Edit the config/config.php file inside the nextcloud_data volume. You can do this by executing a command inside the container.
docker exec -it nextcloud-app sh -c "cat > /var/www/html/config/config.php"
This will open the file. Add your server IP to the trusted_domains array. The file should look like this:
'trusted_domains' =>
array (
0 => 'cloud.example.com',
1 => 'your_server_ip',
),
Save and exit. The web server will automatically reload the config.
Step 8: Finish and Verify in the Web UI
Go back to your browser and log in. Navigate to Settings > Overview. You should see a green checkmark or warnings that need to be addressed. The most common warning is about the missing imagick PHP module for preview generation. We will address this in the hardening section, but for now, the core installation is complete.
Advanced Setup and Optimization
Reverse Proxy and SSL with Caddy
Exposing Nextcloud directly on port 8080 is not secure. We will use a reverse proxy to handle HTTPS. Caddy is an excellent choice because it automatically obtains and renews Let's Encrypt certificates. We will run it as a separate container on the same Docker network.
Step 1: Create a Caddyfile
Create a directory for Caddy and its configuration.
mkdir -p ~/caddy && cd ~/caddy && \
touch Caddyfile
Edit the Caddyfile with your domain.
cloud.example.com {
reverse_proxy nextcloud-app:80
}
Step 2: Add Caddy to the Docker Network
You need to connect the Caddy container to the same Docker network as your Nextcloud stack. By default, docker compose creates a network named <project_name>_default. Identify your network name with docker network ls. Then, modify your docker-compose.yml in ~/nextcloud to use a fixed network name.
Add the following to your docker-compose.yml:
networks:
default:
name: nextcloud_network
Step 3: Create caddy docker-compose.yml
In the ~/caddy directory, create a docker-compose.yml for Caddy.
services:
caddy:
image: caddy:2-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- nextcloud_network
volumes:
caddy_data:
caddy_config:
networks:
nextcloud_network:
external: true
Step 4: Deploy Caddy and Update Nextcloud Config
Start Caddy.
cd ~/caddy && \
docker compose up -d
Now, access your Nextcloud instance via https://cloud.example.com. You will get a secure connection. Nextcloud will automatically detect the new trusted domain if you have already set it in the config. If not, add it to the trusted_domains array as shown in Step 7.
Backups
The most critical part of any self-hosted service is a reliable backup strategy. We will back up the database and the data directory. Create a script that uses pg_dump to create a SQL dump and tar to archive the data.
mkdir -p ~/backup-scripts && cd ~/backup-scripts && \
nano backup.sh
Paste the following script. Adjust the paths and passwords.
#!/bin/bash
# Database backup
docker exec nextcloud-db pg_dump -U nextcloud nextcloud > ~/backups/nextcloud-db-$(date +%Y%m%d).sql
# Data backup
tar -czf ~/backups/nextcloud-data-$(date +%Y%m%d).tar.gz /path/to/your/nextcloud-data
echo "Backup completed on $(date)"
Make the script executable and create a cron job to run it daily.
chmod +x ~/backup-scripts/backup.sh && \
crontab -e
Add the following line to run the backup at 2 AM daily.
0 2 * * * ~/backup-scripts/backup.sh
Optional Hardening
The following settings increase security but can break your setup if not adapted to your environment. They are not included in the main docker-compose.yml because they require careful tuning. Do not copy these blindly.
| Setting | Description | Risk |
|---|---|---|
read_only: true |
Makes the container's filesystem read-only. | Nextcloud needs to write to /var/www/html for logs and updates. You must mount specific directories as writable volumes. |
cap_drop: ["ALL"] |
Drops all Linux capabilities. | The container might lose the ability to bind to port 80 or change file ownership. You must add back specific capabilities like NET_BIND_SERVICE. |
security_opt: ["no-new-privileges:true"] |
Prevents privilege escalation. | This is generally safe, but verify your PHP-FPM configuration does not rely on setuid. |
If you want to explore these, add them to your app service definition and test thoroughly. For example, to make the container read-only, you would need to add a writable volume for the data directory and the config directory.
read_only: true
tmpfs:
- /tmp
volumes:
- nextcloud_data:/var/www/html
Troubleshooting Common Errors
| Error | Cause | Solution |
|---|---|---|
502 Bad Gateway from Caddy |
The Nextcloud app container is not reachable or is down. | Check with docker compose ps that the app service is running. Then, check logs with docker compose logs app. Ensure the network name is correct in Caddy's compose file. |
Trusted domain error when accessing via IP |
Your IP is not in the trusted_domains array. |
Edit config/config.php inside the container and add your IP address. |
Internal Server Error after a config change |
Incorrect PHP syntax or wrong file permissions in config.php. |
Run docker exec -it nextcloud-app php -l /var/www/html/config/config.php to check syntax. Ensure the file is owned by www-data. |
| Database connection error | PostgreSQL container is not healthy or credentials are wrong. | Check the .env file for typos. Check the database health with docker inspect nextcloud-db. Restart the stack with docker compose down && docker compose up -d. |
Unable to write to the config directory |
The data directory is not writable by the web server user. |
Run id -u www-data inside the container and chown -R the data directory on your host to that UID. |
| Redis connection error | Redis container is not running or is unhealthy. | Check docker compose ps for the Redis health status. Verify the REDIS_HOST environment variable is set to redis. |
Conclusion and FAQ
You now have a fully functional, self-hosted Nextcloud v34.0.3 instance running on Docker Compose with PostgreSQL and Redis. You have configured a reverse proxy with automatic SSL, set up cron jobs, and implemented a basic backup strategy. This setup is robust and ready for daily use.
Frequently Asked Questions
1. How do I update Nextcloud to a new version?
To update, first back up your database and data directory. Then, pull the new image by changing the NEXTCLOUD_VERSION variable in your .env file to the new version tag, or simply run docker compose pull app. After that, run docker compose up -d to recreate the container with the new image. Finally, run the upgrade routine by visiting https://cloud.example.com and following the prompts. Always check the official Nextcloud release notes for any specific upgrade instructions.
2. Why is my Nextcloud instance slow?
Slowness is commonly reported when Redis is not configured or when the database and app are on the same disk without sufficient I/O. Ensure Redis is running and is being used for caching. Check that your database and data directory are on fast storage (SSD). Also, review the Settings > Overview page for warnings about missing indexes or memory caching. Increasing PHP_MEMORY_LIMIT in your environment variables can also help.
3. Can I use SQLite instead of PostgreSQL?
Yes, for a very small instance with one user, SQLite is simpler. However, PostgreSQL is significantly more scalable and supports concurrent writes better. The official Nextcloud documentation recommends PostgreSQL or MySQL for production. If you are just testing, you can remove the db service and set the SQLITE_DATABASE environment variable, but this guide is for a scalable setup.
4. How do I move my Nextcloud installation to a new server?
The process involves migrating your data and database. First, stop the services on the old server. Then, copy the nextcloud_data volume and the database dump to the new server. Restore the database with psql and start the new stack. You will need to update the trusted_domains and possibly the IP address in your reverse proxy configuration. The official Nextcloud documentation has a detailed migration guide.
5. Is it safe to expose Nextcloud to the internet?
Yes, if you follow security best practices. This includes using HTTPS via a reverse proxy, keeping your Nextcloud version up to date, setting strong passwords, and enabling two-factor authentication for your users. The Optional Hardening section in this guide provides additional measures. Regularly check Nextcloud's security advisories and apply updates promptly.