Self-hosted • Privacy-first • No tracking
Home / Homelab / Self-Host Nextcloud in 2026: The Complete Docker Compose Guide
Homelab #self-hosted#homelab#docker-compose#nextcloud#cloud-storage ⏱ 13 min • 👁 2 • Sep 02, 2026

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.

AdSense — Top (970x90) • Responsive
Self-Host Nextcloud in 2026: The Complete Docker Compose Guide

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.

AdSense — In-article (responsive)

Related Guides