Skip to content

Self host your data. Nextcloud for the win

    Self-hosting a personal cloud like Nextcloud gives you unparalleled control over your data. But setting it up to be reliable, secure, and maintainable can be a challenge. That’s where Docker Compose shines!

    The docker-compose.yaml file you’re looking at is an excellent example of a modern, production-ready Nextcloud stack. It goes far beyond the basics, incorporating key services for database, caching, scheduled tasks, automatic updates, and robust security.

    Let’s break down this powerful configuration piece by piece.


    The Core Trio: Nextcloud, Database, and Cache

    Any powerful web application needs a solid foundation, and this setup provides exactly that with three essential services:

    1. db: The Data Workhorse (MariaDB)

    Nextcloud needs a database to store all its file metadata, user information, and configurations. This setup chooses MariaDB:11, a community-developed fork of MySQL known for its performance and reliability.

    • Custom Commands: The command section sets specific SQL isolation and logging parameters (--transaction-isolation=READ-COMMITTED, --binlog-format=ROW), which are best practices for high-integrity applications like Nextcloud.
    • Security: All sensitive credentials (MYSQL_ROOT_PASSWORD, MYSQL_PASSWORD) are loaded securely from external environment variables (prefixed with ${...}).
    • Persistence: A named volume (db:/var/lib/mysql) ensures your precious data persists even if the container is rebuilt.
    • Health Check: A healthcheck guarantees the application container only starts when the database is fully ready to accept connections.

    2. redis: The Speed Booster (Redis)

    Nextcloud heavily benefits from an in-memory object cache to speed up common operations. The Redis:alpine container serves this purpose, acting as a lightning-fast intermediary between the Nextcloud application and the database.

    3. nextcloud: The Application (Nextcloud:apache)

    This is the heart of the stack—your personal cloud application.

    • Dependencies: The depends_on block is crucial here. It waits for both db and redis to pass their service_healthy checks before starting, preventing frustrating startup errors.
    • Networking: The ports entry exposes the Nextcloud service (usually on port 80 in the container) to a custom port on your host system, like 8080, using ${NEXTCLOUD_HTTP_PORT:-8080}:80.
    • Storage: It uses two types of volumes:
      • A named volume (nextcloud:/var/www/html) for the application files and configuration.
      • A bind mount (./data:/var/www/html/data) to link the actual user files to a directory on your host machine (./data), making backups and external management easier.
    • Object Storage: The configuration includes numerous Amazon S3-style environment variables (OBJECTSTORE_S3_...). This indicates the setup is ready to offload its primary file storage to an object storage service, like AWS S3 or MinIO, for massive scalability.

    Automation and Maintenance Services

    This configuration includes two services that make maintaining the stack a breeze.

    4. cron: Scheduled Tasks

    Nextcloud needs background jobs (like checking for updates, moving trash, and processing notifications) to run regularly. This separate cron service runs the same Nextcloud image but with a different entrypoint (/cron.sh) to execute these tasks on a schedule without interfering with the main web application. It also waits for db and redis to be healthy.

    5. watchtower: Automatic Updates

    The Watchtower container is a fantastic maintenance tool. It monitors your running containers and automatically pulls the latest images and restarts the containers when updates are available. This ensures your stack stays current with minimal manual intervention. It’s configured to run on a CRON schedule (e.g., daily at midnight) and includes WATCHTOWER_CLEANUP: "true" to remove old, unused images.


    Security First: The CrowdSec Integration 🛡️

    What truly sets this setup apart is the inclusion of CrowdSec, a modern, collaborative intrusion prevention system. This adds a crucial layer of security to your Nextcloud instance.

    6. crowdsec: The Brain

    This service acts as the security agent.

    • Logs Analysis: It’s configured to monitor various logs by mounting /var/log from the host. Critically, the COLLECTIONS environment variable tells it to actively look for threats in Linux, Apache2, and Nextcloud logs.
    • Labeling: Notice the nextcloud service has a label: - "crowdsec.enable=true". This is how CrowdSec knows to inspect that container’s logs for bad behavior.
    • Banning: With cap_add: NET_ADMIN, CrowdSec has the power to ban malicious IPs at the network firewall level.

    7. crowdsec-bouncer: The Enforcer

    The bouncer is the intermediary that takes the banning decisions from the crowdsec agent and applies them to your services. In this configuration, it’s set up to work as a generic bouncer, likely intended to be integrated with an external reverse proxy (like Traefik, hence the image name) to block traffic before it even hits Nextcloud. It relies on a secure API key (CROWDSEC_BOUNCER_API_KEY) to communicate with the CrowdSec agent.


    Conclusion: A Nextcloud Powerhouse

    This docker-compose.yaml file isn’t just a basic Nextcloud install; it’s a self-healing, highly performant, and automatically secured private cloud solution. By leveraging separate containers for the database, cache, cron jobs, and a complete security suite, you get maximum stability and security for your self-hosted data.

    Ready to deploy this powerhouse? You’ll just need to create a .env file to define all those necessary environment variables, especially the passwords! Happy self-hosting! 🚀

    services:
      db:
        image: mariadb:11
        container_name: nextcloud-db
        restart: unless-stopped
        command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --log-bin=binlog
        environment:
          MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
          MYSQL_PASSWORD: ${MYSQL_PASSWORD}
          MYSQL_DATABASE: nextcloud
          MYSQL_USER: nextcloud
        volumes:
          - db:/var/lib/mysql
        networks:
          - nextcloud_net
        healthcheck:
          test: ["CMD", "mariadb", "-h", "localhost", "-u", "nextcloud", "-p${MYSQL_PASSWORD}", "-e", "SELECT 1"]
          interval: 10s
          timeout: 5s
          retries: 5
    
      redis:
        image: redis:alpine
        container_name: nextcloud-redis
        restart: unless-stopped
        volumes:
          - redis_data:/data
        networks:
          - nextcloud_net
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
          interval: 10s
          timeout: 5s
          retries: 5
    
      nextcloud:
        image: nextcloud:apache
        container_name: nextcloud-app
        restart: unless-stopped
        ports:
          - "${NEXTCLOUD_HTTP_PORT:-8080}:80"
        depends_on:
          db:
            condition: service_healthy
          redis:
            condition: service_healthy
        environment:
          # Database
          MYSQL_HOST: db
          MYSQL_DATABASE: nextcloud
          MYSQL_USER: nextcloud
          MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    
          # Redis (local)
          REDIS_HOST: redis
    
          # S3 / object store (primary or external)
          OBJECTSTORE_S3_BUCKET: ${OBJECTSTORE_S3_BUCKET:-}
          OBJECTSTORE_S3_REGION: ${OBJECTSTORE_S3_REGION:-us-east-1}
          OBJECTSTORE_S3_HOST: ${OBJECTSTORE_S3_HOST:-s3.amazonaws.com}
          OBJECTSTORE_S3_PORT: ${OBJECTSTORE_S3_PORT:-443}
          OBJECTSTORE_S3_KEY: ${OBJECTSTORE_S3_KEY:-}
          OBJECTSTORE_S3_SECRET: ${OBJECTSTORE_S3_SECRET:-}
          OBJECTSTORE_S3_SSL: ${OBJECTSTORE_S3_SSL:-false}
          OBJECTSTORE_S3_USEPATH_STYLE: ${OBJECTSTORE_S3_USEPATH:-true}
          OBJECTSTORE_S3_LEGACYAUTH: false
          # Nextcloud admin & proxy
          NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER:-admin}
          NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD:-}
          NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_TRUSTED_DOMAINS:-localhost}
          TRUSTED_PROXIES: ${TRUSTED_PROXIES:-}
          OVERWRITEPROTOCOL: ${OVERWRITEPROTOCOL:-https}
    
        volumes:
          # PHP files + config
          - nextcloud:/var/www/html
          # optional bind mount for persistent raw data folder beside the volume
          - ./data:/var/www/html/data
        networks:
          - nextcloud_net
        labels:
          - "crowdsec.enable=true"
        healthcheck:
          test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
          interval: 30s
          timeout: 10s
          retries: 3
    
      cron:
        image: nextcloud:apache
        container_name: nextcloud-cron
        restart: unless-stopped
        entrypoint: /cron.sh
        depends_on:
          db:
            condition: service_healthy
          redis:
            condition: service_healthy
        environment:
          MYSQL_HOST: db
          MYSQL_DATABASE: nextcloud
          MYSQL_USER: nextcloud
          MYSQL_PASSWORD: ${MYSQL_PASSWORD}
          REDIS_HOST: redis
          # mirror relevant S3 envs for object storage if used by cron jobs
          OBJECTSTORE_S3_BUCKET: ${OBJECTSTORE_S3_BUCKET:-}
          OBJECTSTORE_S3_REGION: ${OBJECTSTORE_S3_REGION:-}
          OBJECTSTORE_S3_HOST: ${OBJECTSTORE_S3_HOST:-}
          OBJECTSTORE_S3_PORT: ${OBJECTSTORE_S3_PORT:-}
          OBJECTSTORE_S3_KEY: ${OBJECTSTORE_S3_KEY:-}
          OBJECTSTORE_S3_SECRET: ${OBJECTSTORE_S3_SECRET:-}
          OBJECTSTORE_S3_AUTOCREATE: ${OBJECTSTORE_S3_AUTOCREATE:-false}
        volumes:
          - nextcloud:/var/www/html
          - ./data:/var/www/html/data
        networks:
          - nextcloud_net
    
      watchtower:
        image: containrrr/watchtower:latest
        container_name: watchtower
        restart: unless-stopped
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock:ro
        environment:
          WATCHTOWER_CLEANUP: "true"
          # Use either schedule CRON or poll interval. CRON example below (daily at midnight):
          WATCHTOWER_SCHEDULE: "${WATCHTOWER_SCHEDULE:-0 0 * * *}"
          WATCHTOWER_INCLUDE_STOPPED: "${WATCHTOWER_INCLUDE_STOPPED:-false}"
          WATCHTOWER_REVIVE_STOPPED: "${WATCHTOWER_REVIVE_STOPPED:-false}"
          WATCHTOWER_NOTIFICATION_URL: ${WATCHTOWER_NOTIFICATION_URL:-}
        networks:
          - nextcloud_net
    
      crowdsec:
        image: crowdsecurity/crowdsec:latest
        container_name: crowdsec
        restart: unless-stopped
        cap_add:
          - NET_ADMIN
        environment:
          COLLECTIONS: ${CROWDSEC_COLLECTIONS:-crowdsecurity/linux crowdsecurity/apache2 crowdsecurity/nextcloud}
          GID: ${CROWDSEC_GID:-1000}
        volumes:
          - crowdsec_data:/var/lib/crowdsec/data
          - ./crowdsec/config:/etc/crowdsec:rw
          - /var/log:/var/log:ro
          - /var/run/docker.sock:/var/run/docker.sock:ro
        ports:
          - "${CROWDSEC_LAPI_PORT:-8081}:8080"
        networks:
          - nextcloud_net
        depends_on:
          - nextcloud
    
      crowdsec-bouncer:
        image: fbonalair/traefik-crowdsec-bouncer:latest
        container_name: crowdsec-bouncer
        restart: unless-stopped
        environment:
          CROWDSEC_BOUNCER_API_KEY: ${CROWDSEC_BOUNCER_API_KEY:-}
          CROWDSEC_AGENT_HOST: crowdsec:8080
        depends_on:
          - crowdsec
        networks:
          - nextcloud_net
    
    networks:
      nextcloud_net:
        driver: bridge
    
    volumes:
      db:
      nextcloud:
      redis_data:
      crowdsec_data:

    Leave a Reply

    Your email address will not be published. Required fields are marked *