Self-hosted • Privacy-first • No tracking
Home / Homelab / Nextcloud Behind Traefik: A Complete Docker Compose Guide (2026)
Homelab #homelab#docker-compose#reverse-proxy#traefik#nextcloud ⏱ 7 min • 👁 5 • Sep 04, 2026

Nextcloud Behind Traefik: A Complete Docker Compose Guide (2026)

Step-by-step guide to deploy Nextcloud v34.0.3 behind Traefik with Docker Compose, including hardening, backups, and troubleshooting.

AdSense — Top (970x90) • Responsive
Nextcloud Behind Traefik: A Complete Docker Compose Guide (2026)

Introduction

Self-hosting Nextcloud is one of the most rewarding projects for a homelab enthusiast. It gives you full control over your files, calendar, contacts, and collaborative editing — all without relying on third-party cloud providers. When paired with Traefik as a reverse proxy, you gain automatic SSL certificates, easy service discovery, and a single entry point for all your self-hosted apps. This guide walks you through deploying Nextcloud v34.0.3 (the latest official release as of August 2026) behind Traefik using Docker Compose. You will learn how to set up the database, configure Traefik labels, enable HTTPS, and avoid common pitfalls that trip up even experienced users. By the end, you'll have a production-ready Nextcloud instance that respects your privacy and runs entirely on your hardware.

Why Traefik? Unlike nginx or Caddy, Traefik integrates natively with Docker's API. It watches for new containers and automatically routes traffic based on labels you define. This makes scaling and adding new services trivial. Combined with Let's Encrypt via the HTTP-01 challenge, Traefik handles SSL certificate issuance and renewal transparently. For a homelab, this means you don't need to manually edit proxy configuration files every time you spin up a new container.

This guide assumes you are comfortable with the command line, have Docker and Docker Compose installed, and own a domain name that points to your server's public IP (or you're using a dynamic DNS service). We'll cover everything from prerequisites to advanced hardening, and we'll also address common errors with solutions so you can troubleshoot confidently.

Important: Always verify the latest Nextcloud version before pinning. The version mentioned here (v34.0.3) was current as of August 2026, but newer releases may exist. Check the official Nextcloud release page before proceeding.

Prerequisites / Requirements

Before you start, ensure your homelab server meets the following minimum requirements. These are typical values based on community reports; your actual usage may vary depending on the number of users and enabled features.

Component Minimum Recommended Notes
CPU 2 cores 4+ cores Nextcloud performs background tasks like preview generation and cron jobs. More cores speed these up.
RAM 2 GB 4-8 GB The database (MariaDB/PostgreSQL) and PHP-FPM consume memory. 4 GB is comfortable for a small household.
Storage 20 GB free SSD, 1TB+ The OS, Docker images, and Nextcloud data. Use an SSD for database performance.
Software Docker 24+ Docker Compose v2 Install via official Docker docs. Also need curl, wget, and a text editor.
Domain A record DNS management A domain or subdomain (e.g., cloud.example.com) pointing to your server's public IP.
Network Stable internet - For SSL certificate issuance and updates.

Optional but recommended: A static IP or dynamic DNS (e.g., DuckDNS) for consistent access.

Step-by-Step Installation

Step 1: Prepare the Environment

First, create a directory for the project and set up a .env file to store secrets. Never commit this file to Git.

mkdir -p ~/nextcloud && cd ~/nextcloud && touch .env && chmod 600 .env

Edit the .env file with your preferred editor and add the following variables. Replace the placeholders with strong passwords and your actual domain.

POSTGRES_PASSWORD=your_strong_db_password_here
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=your_strong_admin_password_here
DOMAIN=cloud.example.com

Security warning: Do not use the same password as your email or any other service. Keep this file secure and never push it to a public repository.

Step 2: Create the Network and External Volumes

Traefik and Nextcloud need to share a Docker network so Traefik can route traffic to the container. Create a dedicated network and also two volumes: one for the database and one for Nextcloud's data.

docker network create traefik-public && docker volume create nextcloud_db_data && docker volume create nextcloud_data

Step 3: Set Up the Traefik Container (If Not Already Running)

If you don't have Traefik running yet, create a docker-compose.traefik.yml file with the following content. This sets up Traefik with the Docker provider and enables the HTTP-01 challenge.

version: "3.8"

services:
  traefik:
    image: traefik:v3.1
    container_name: traefik
    restart: unless-stopped
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.email=your_email@example.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "traefik-data:/letsencrypt"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"

volumes:
  traefik-data:

Replace your_email@example.com with a valid email for Let's Encrypt expiration notices. Then start Traefik:

docker compose -f docker-compose.traefik.yml up -d

Step 4: Create the Nextcloud Docker Compose File

Now create docker-compose.yml for Nextcloud. We'll use PostgreSQL as the database because it's well-supported and performs better than SQLite for multi-user setups. The following compose file includes environment variables from .env.

version: "3.8"

services:
  db:
    image: postgres:16-alpine
    container_name: nextcloud_db
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
    volumes:
      - nextcloud_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "nextcloud"]
      interval: 10s
      timeout: 5s
      retries: 5

  app:
    image: nextcloud:34.0.3
    container_name: nextcloud_app
    restart: unless-stopped
    environment:
      POSTGRES_HOST: db
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER}
      NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
      NEXTCLOUD_TRUSTED_DOMAINS: ${DOMAIN}
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - nextcloud_data:/var/www/html
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nextcloud.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.nextcloud.entrypoints=websecure"
      - "traefik.http.routers.nextcloud.tls.certresolver=letsencrypt"
      - "traefik.http.services.nextcloud.loadbalancer.server.port=80"
      - "traefik.http.routers.nextcloud.middlewares=nextcloud-secure-headers"
      - "traefik.http.middlewares.nextcloud-secure-headers.headers.stsSeconds=31536000"
      - "traefik.http.middlewares.nextcloud-secure-headers.headers.stsIncludeSubdomains=true"
      - "traefik.http.middlewares.nextcloud-secure-headers.headers.stsPreload=true"
      - "traefik.http.middlewares.nextcloud-secure-headers.headers.contentTypeNosniff=true"
      - "traefik.http.middlewares.nextcloud-secure-headers.headers.frameDeny=true"

volumes:
  nextcloud_db_data:
    external: true
  nextcloud_data:
    external: true

Note: The image nextcloud:34.0.3 is pinned. If you prefer to use a variable for easier updates, change it to image: nextcloud:${NEXTCLOUD_VERSION:-latest} and add NEXTCLOUD_VERSION=34.0.3 to your .env. Always check the official release page before pinning a new version.

Step 5: Start the Stack

Now that the compose file is ready, start the containers. The first run will pull the images and initialize the database, which may take a few minutes.

docker compose up -d

After it finishes, check the logs to ensure everything started correctly:

docker compose logs app && docker compose logs db

Step 6: Verify Nextcloud is Accessible

Open your browser and navigate to https://${DOMAIN} (replace with your actual domain). You should see the Nextcloud setup page. Log in with the admin username and password you set in the .env file. If you see a security warning, it may be because your domain isn't recognized yet — we'll fix that in the advanced settings.

Step 7: Configure Nextcloud's Trusted Domains and Overwrite Protocol

Even though we set NEXTCLOUD_TRUSTED_DOMAINS, we need to adjust config.php to handle the reverse proxy correctly. Edit the config file inside the container:

docker exec -it nextcloud_app bash -c "cat > /var/www/html/config/config.php" <<EOF
<?php
\$CONFIG = array (
  'trusted_domains' =>
  array (
    0 => 'localhost',
    1 => '${DOMAIN}',
  ),
  'overwriteprotocol' => 'https',
  'overwritehost' => '${DOMAIN}',
  'overwrite.cli.url' => 'https://${DOMAIN}',
);
EOF

Note: The above command overwrites the entire config.php. If you already have settings, merge them instead. After making changes, restart the app container:

docker restart nextcloud_app

Step 8: Enable Background Jobs (Cron)

Nextcloud needs cron jobs for background tasks like file scanning and notifications. By default, it uses AJAX, which is not reliable. Switch to cron by running the following command inside the container:

docker exec -u www-data nextcloud_app php occ background:cron

Then add a cron job on the host that runs every 5 minutes:

(crontab -l 2>/dev/null; echo "*/5 * * * * docker exec -u www-data nextcloud_app php -f /var/www/html/cron.php") | crontab -

Step 9: Set Up Redis for Caching (Recommended)

Redis improves performance for file locking and caching. Add a Redis service to your compose file and configure Nextcloud to use it.

Edit docker-compose.yml and add the following service:

  redis:
    image: redis:7-alpine
    container_name: nextcloud_redis
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD}

Add REDIS_PASSWORD=your_strong_redis_password_here to your .env. Then add the environment variable and volume to the app service:

    environment:
      REDIS_HOST: redis
      REDIS_PASSWORD: ${REDIS_PASSWORD}

Restart the stack:

docker compose up -d && docker restart nextcloud_app

Step 10: Configure Nextcloud to Use Redis

Run the following command to set Redis as the caching backend:

docker exec -u www-data nextcloud_app php occ config:system:set memcache.local --value '\OC\Memcache\Redis' && docker exec -u www-data nextcloud_app php occ config:system:set memcache.distributed --value '\OC\Memcache\Redis' && docker exec -u www-data nextcloud_app php occ config:system:set memcache.locking --value '\OC\Memcache\Redis' && docker exec -u www-data nextcloud_app php occ config:system:set redis host --value 'redis' && docker exec -u www-data nextcloud_app php occ config:system:set redis password --value '${REDIS_PASSWORD}'

Step 11: Verify Everything is Working

Run Nextcloud's built-in status check:

docker exec -u www-data nextcloud_app php occ status && docker exec -u www-data nextcloud_app php occ maintenance:mode --off

Visit your domain again and confirm that the admin page shows no warnings. If you see any, address them in the next section.

Step 12: Set Up Automated Backups

Backups are critical. Use occ to export the database and copy the config and data directories. Create a backup script backup.sh:

#!/bin/bash
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p ~/nextcloud/backups/$TIMESTAMP

docker exec nextcloud_db pg_dump -U nextcloud nextcloud > ~/nextcloud/backups/$TIMESTAMP/db.sql
docker cp nextcloud_app:/var/www/html/config ~/nextcloud/backups/$TIMESTAMP/config
docker cp nextcloud_app:/var/www/html/data ~/nextcloud/backups/$TIMESTAMP/data

echo "Backup completed at $TIMESTAMP"

Make it executable and run it:

chmod +x backup.sh && ./backup.sh

Add a cron job to run it daily.

Advanced Configuration / Optimization

Reverse Proxy and SSL

You already have Traefik handling SSL. To enforce HTTPS-only, add a redirect middleware to your Traefik router:

labels:
  - "traefik.http.routers.nextcloud.middlewares=nextcloud-secure-headers,nextcloud-redirect"
  - "traefik.http.middlewares.nextcloud-redirect.redirectscheme.scheme=https"
  - "traefik.http.middlewares.nextcloud-redirect.redirectscheme.permanent=true"

Backup Strategy

Use the script from Step 12 and also consider off-site backups using rclone. Test your backups regularly by restoring them in a temporary container.

Security Hardening (Optional)

The following are advanced hardening measures. They may break your setup if copied blindly — adapt them to your environment and test thoroughly.

  • Run containers with read-only root filesystems: Add read_only: true to the app service. Nextcloud writes to /var/www/html, so you'll need to mount a tmpfs for writable directories like /tmp.
  • Drop Linux capabilities: Add cap_drop: [ALL] and cap_add: [NET_BIND_SERVICE] to the app service. This limits the container's privileges. Test carefully.
  • Use a dedicated user: In the app service, set user: "${PUID}:${PGID}" where PUID/PGID are your host user IDs. Check your host with id -u && id -g. The default user in the Nextcloud image is www-data (UID 33). Verify against the image documentation.

Troubleshooting Common Errors

Error Cause Solution
502 Bad Gateway from Traefik The Nextcloud container is not ready or the port is wrong Check docker compose ps and logs. Ensure the loadbalancer port is 80.
Trusted domain error The domain isn't listed in config.php Edit config.php and add your domain to trusted_domains. Restart.
Database connection refused The DB container isn't healthy or password mismatch Verify .env has correct POSTGRES_PASSWORD and that db is healthy with docker compose ps.
Unable to write to config directory Permissions issue in the nextcloud_data volume Run docker exec -u root nextcloud_app chown -R www-data:www-data /var/www/html and restart.
SSL certificate error Let's Encrypt validation failed Check Traefik logs. Ensure port 80 is reachable from the internet and the domain points to your server.
Redis connection error Redis password mismatch or host unreachable Verify REDIS_PASSWORD in .env and that the redis service is running.
Cron job not running The cron entry is wrong or container name changed Use docker exec -u www-data nextcloud_app php -f /var/www/html/cron.php manually to test.

Conclusion & FAQ

You now have a fully functional Nextcloud instance behind Traefik with automatic SSL, a PostgreSQL database, Redis caching, and cron-based background jobs. This setup provides a solid foundation for a privacy-respecting cloud service. Remember to keep your system updated, monitor logs, and perform regular backups.

FAQ

Q1: Can I use SQLite instead of PostgreSQL? Yes, but SQLite is only recommended for testing or single-user setups. PostgreSQL handles concurrent writes much better and is the recommended database for production Nextcloud deployments.

Q2: How do I update Nextcloud to a new version? Pull the new image, update the version tag in your compose file, and run docker compose up -d. Then execute docker exec -u www-data nextcloud_app php occ upgrade to complete the database migration.

Q3: What if I don't have a domain name? You can use a dynamic DNS service like DuckDNS or use a self-signed certificate with Traefik's insecureskipverify setting, but that is not recommended for anything beyond local testing.

Q4: How can I access Nextcloud from outside my home network? Ensure your router forwards ports 80 and 443 to your server, and that your domain points to your public IP. Consider using a VPN for additional security.

Q5: Can I add more storage later? Yes, you can mount additional disks to the nextcloud_data volume. Use the external volume feature and mount the new disk to the same path, then run occ files:scan --all to index the new files.

AdSense — In-article (responsive)

Related Guides