Install Nextcloud on a VPS: A Guide with Docker

Set up your own cloud with Nextcloud on a VPS. ✓ Detailed instructions ✓ With Docker & Caddy ✓ Suitable for beginners

Install Nextcloud on a VPS: A Guide with Docker-heroimage

Introduction

Want to set up your own personal cloud? In this step-by-step guide, I’ll show you how to install Nextcloud on a VPS (Virtual Private Server). I’ve also created a German YouTube video that walks you through the process:

Install Nextcloud on a VPS (Link to Youtube)

Below you’ll find a summary of the steps shown in the video.

Nextcloud Server: A Platform for Data Sovereignty

Nextcloud is open-source software for self-hosting files and data, offering a variety of features such as file synchronization, calendar, contacts, email, and more. It can be used as an alternative to third-party cloud services like Dropbox or Google Drive.

Development History

A brief overview of Nextcloud’s development:

  • v9.0.50

    Erster Nextcloud Release nach der Abspaltung von ownCloud.

  • v10.0.0

    Neue Funktionen für Systemadministratoren, um den Datenfluss zwischen Benutzern zu steuern und zu lenken.

  • v11.0.0

    Volltextsuche als Gamechanger

  • v13.0.0

    -

  • v19.0.0

    -

  • v20.0.0

    -

  • v23.0.0

    -

  • v24.0.0

    Benutzer-Migration leicht gemacht.

  • v25.0.0

    -

  • v26.0.0

    -

  • v27.0.0

    -

  • v28.0.0

    Verbesserte Benutzeroberfläche, Leistungsoptimierungen, Fortschritte in der Sicherheit und Benutzerfreundlichkeit.

  • v29.0.0

    -

  • v30.0.0

    Neuste Version mit vielen Verbesserungen.

  • More Than Just File Storage

    Nextcloud is like a digital hub, not only facilitating file storage but also offering a whole range of other functions:

    • File Synchronization and Sharing: Nextcloud allows you to synchronize files between different devices and easily share them with other users.
    • Document Collaboration: The integrated office suite enables real-time collaborative editing of documents.
    • Calendar, Contacts, and Email: Nextcloud offers features for managing calendars, contacts, and emails.
    • Variety of Apps: An app store provides a wide selection of additional features that extend the server’s functionality.

    Reasons for Installing on a VPS

    Installing on a VPS is particularly practical for several reasons:

    Centralized Access

    A VPS allows you to access your documents from anywhere with an internet connection. You’re not limited to your local network, enabling access from anywhere.

    Data Security

    Your data is securely stored in professional data centers—protected from power outages, fires, or other physical risks.

    Scalability

    With a VPS, you can adjust server resources as your needs grow—meaning more storage space or computing power when required.

    Professional Hosting Environment

    A VPS offers a professional hosting environment with dedicated IP addresses and a fast internet connection.

    These aspects make using a VPS for a paperless setup an attractive option for businesses and individuals looking for an efficient and secure solution for their document management.

    Prerequisites

    • Computer with Visual Studio Code: For working on the server.
    • A Server where Docker can be installed (this is software that makes installation much easier).
    • Domain with the option to create subdomains: For accessing Nextcloud.
    • Mail Server with SMTP Support: Optional for notifications.

    Rent a Server

    First, we need a server. I recommend renting a VPS from a provider of your choice. I personally use Netcup. They offer a simple and fast way to rent a VPS.

    You can also check here for vouchers.

    The costs currently start at €3.99 per month, plus an annoying newly introduced setup fee of €5. I find this counterproductive for VPSs, as they should offer a way to start quickly and flexibly. The Root Servers (from €9.81 per month) offer significantly better performance due to the dedicated CPU cores.

    In the future, I will probably use Hetzner more often for testing, as they don’t have a setup fee.

    Once you’ve rented a VPS, you’ll find the IP address in the CCP. Otherwise, it should also be in the email.

    Netcup Control Panel

    Rent a Domain

    Next, we need a domain. Again, you should use a provider of your choice. The domain and server don’t have to be with the same provider. However, I also use Netcup for this. Here you have two options:

    1. Rent an additional domain.
    2. Rent a web hosting package with an included domain.

    The additional domain has the advantage of being cheaper (currently €5.04 per year for a .de domain). However, the web hosting packages (from €2.17 per month) offer you additional features such as email mailboxes and webspace.

    Configure the Domain

    After renting the domain, you need to configure it. Log in to the CCP and click on “Domains.” Select your domain and click on “DNS.”

    Netcup DNS

    Add two new A records. The host is ”@” (for the main domain) and ”*” (for all subdomains). Enter your server’s IP address as the target.

    If you have the web hosting package, first delete the entries for ”@” and ”*” for both the A records and the AAAA records. The rest must remain as it is for the email mailboxes and webspace to function.

    Now you have to wait until the DNS changes have propagated everywhere. This can take up to 24 hours. You can test it in the terminal with ping your-domain.de. If your server’s IP address is returned, the domain is ready.

    Reinstall the Server

    You can now log in to the Server Control Panel and select your server. Under “Media” and then “Images,” you can choose a new operating system. I recommend Ubuntu 24.04 LTS (updates until April 2029).

    Netcup Reinstall Server

    You should create a user during the installation.

    Establish an SSH Connection

    After the installation is complete, you can connect to your server via SSH. We’ll use VSCode for this. Install the Remote - SSH plugin and click on the icon in the bottom left corner. There you can select “Remote-SSH: Connect to Host…” and configure your SSH connection. The command ssh user@ip should also connect you.

    Once connected, you can open your user’s home directory in the explorer and edit your files there. You can also use the terminal to execute commands:

    # Update system packages
    sudo apt update     # Update package lists
    sudo apt upgrade    # Upgrade installed packages
    

    Install Docker and Docker Compose

    Next, we install Docker. Execute the following commands:

    # Add Docker's official GPG key:
    sudo apt-get update
    sudo apt-get install ca-certificates curl
    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc
    
    # Add the repository to Apt sources:
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
      $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt-get update
    
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    

    Prepare Caddy

    We’ll use Caddy as a reverse proxy to make Nextcloud accessible via HTTPS.

    First, we need a network to connect Caddy to the other containers:

    sudo docker network create proxy
    

    Now, create a folder for Caddy and the necessary files:

    mkdir -p caddy/{data,config}
    touch caddy/docker-compose.yml caddy/Caddyfile
    

    Enter the following into the docker-compose.yml file:

    networks:
        proxy:
            external: true
            name: proxy
    
    services:
        caddy:
            image: caddy:2
            container_name: caddy
            restart: unless-stopped
            ports:
                - 80:80
                - 443:443
            volumes:
                - ./data:/data
                - ./config:/config
                - ./Caddyfile:/etc/caddy/Caddyfile:ro
            networks:
                - proxy
    

    Install Nextcloud

    Perfect! The foundation is laid. Let’s get started with the actual Nextcloud installation. Create a new folder and the necessary files:

    mkdir -p nextcloud/data
    touch nextcloud/docker-compose.yml
    

    Enter the following into the nextcloud/docker-compose.yml file:

    networks:
        proxy:
            external: true
            name: proxy
    
    services:
        nextcloud:
            container_name: nextcloud
            image: nextcloud
            restart: always
            volumes:
                - ./data:/var/www/html/
            networks:
                - proxy
    

    Now, start the container:

    sudo docker compose -f nextcloud/docker-compose.yml up -d
    

    Configure the Nextcloud Proxy

    Finally, enter the configuration for Nextcloud into the Caddyfile:

    {
    	acme_ca https://acme-v02.api.letsencrypt.org/directory
    	email mail@deployn.de
    }
    
    nextcloud.pixan.de {
    	reverse_proxy nextcloud:80
    }
    

    And start Caddy:

    sudo docker compose -f caddy/docker-compose.yml up -d
    

    Configure Nextcloud

    Now you can log in under the domain and configure Nextcloud. Create an admin user. At this point, we could add other users and upload files. However, we won’t do that now; instead, we’ll optimize the installation.

    💡 Common problems

    • If the domain is not reachable: Check DNS entries and propagation.
    • If you have connection problems: Check firewall rules.
    • If Nextcloud doesn’t start: Check container logs with docker logs nextcloud.

    Create an Alias

    We will be deleting and reinstalling Nextcloud a few more times. To avoid typing the same commands repeatedly, let’s create an alias. Open the .bashrc file and add the following at the very bottom:

    alias caddy-reload='sudo docker compose -f ~/caddy/docker-compose.yml exec caddy caddy reload --config /etc/caddy/Caddyfile'
    alias caddy-down='sudo docker compose -f ~/caddy/docker-compose.yml down'
    alias caddy-up='sudo docker compose -f ~/caddy/docker-compose.yml up -d'
    alias nc-down='sudo docker compose -f ~/nextcloud/docker-compose.yml down'
    alias nc-up='sudo docker compose -f ~/nextcloud/docker-compose.yml up -d'
    alias nc-remove='sudo rm -rf ~/nextcloud/data'
    alias nc-init='mkdir -p ~/nextcloud/data'
    

    Then run source ~/.bashrc to apply the changes.

    nc-down
    nc-remove
    

    Nextcloud is deleted again, and we can start over.

    Add a Database

    We don’t want to use SQLite, so let’s add a database. Update our aliases:

    alias caddy-reload='sudo docker compose -f ~/caddy/docker-compose.yml exec caddy caddy reload --config /etc/caddy/Caddyfile'
    alias caddy-down='sudo docker compose -f ~/caddy/docker-compose.yml down'
    alias caddy-up='sudo docker compose -f ~/caddy/docker-compose.yml up -d'
    alias nc-down='sudo docker compose -f ~/nextcloud/docker-compose.yml down'
    alias nc-up='sudo docker compose -f ~/nextcloud/docker-compose.yml up -d'
    alias nc-remove='sudo rm -rf ~/nextcloud/{data,db}'
    alias nc-init='mkdir -p ~/nextcloud/{data,db}'
    

    Apply the changes:

    source ~/.bashrc
    nc-init
    

    Now, update the docker-compose.yml file in the nextcloud directory:

    networks:
        proxy:
            external: true
            name: proxy
        internal:
            external: false
    
    services:
        nextcloud:
            container_name: nextcloud
            image: nextcloud
            restart: always
            volumes:
                - ./data:/var/www/html/
            networks:
                - proxy
                - internal
            environment:
                - MYSQL_HOST=db
                - MYSQL_DATABASE=nextcloud
                - MYSQL_USER=nextcloud
                - MYSQL_PASSWORD=nextcloud_password
            depends_on:
                - db
        db:
            container_name: nextcloud-db
            image: mariadb:11.4
            restart: unless-stopped
            volumes:
                - ./db:/var/lib/mysql
            networks:
                - internal
            environment:
                - MYSQL_ROOT_PASSWORD=root_password
                - MYSQL_DATABASE=nextcloud
                - MYSQL_USER=nextcloud
                - MYSQL_PASSWORD=nextcloud_password
    

    Start the containers:

    nc-up
    

    Adding Redis

    Let’s enhance performance by adding Redis. Delete everything again:

    nc-down
    nc-remove
    

    Update the aliases:

    alias caddy-reload='sudo docker compose -f ~/caddy/docker-compose.yml exec caddy caddy reload --config /etc/caddy/Caddyfile'
    alias caddy-down='sudo docker compose -f ~/caddy/docker-compose.yml down'
    alias caddy-up='sudo docker compose -f ~/caddy/docker-compose.yml up -d'
    alias nc-down='sudo docker compose -f ~/nextcloud/docker-compose.yml down'
    alias nc-up='sudo docker compose -f ~/nextcloud/docker-compose.yml up -d'
    alias nc-remove='sudo rm -rf ~/nextcloud/{data,db,redis}'
    alias nc-init='mkdir -p ~/nextcloud/{data,db,redis}'
    

    Apply the changes:

    source ~/.bashrc
    nc-init
    

    We need our UserID and GroupID:

    id
    

    Update the docker-compose.yml file:

    networks:
        proxy:
            external: true
            name: proxy
        internal:
            external: false
    
    services:
        nextcloud:
            container_name: nextcloud
            image: nextcloud
            restart: always
            volumes:
                - ./data:/var/www/html/
            networks:
                - proxy
                - internal
            environment:
                - MYSQL_HOST=db
                - MYSQL_DATABASE=nextcloud
                - MYSQL_USER=nextcloud
                - MYSQL_PASSWORD=nextcloud_password
                - REDIS_HOST=redis
            depends_on:
                - db
                - redis
        db:
            container_name: nextcloud-db
            image: mariadb:11.4
            restart: unless-stopped
            volumes:
                - ./db:/var/lib/mysql
            networks:
                - internal
            environment:
                - MYSQL_ROOT_PASSWORD=root_password
                - MYSQL_DATABASE=nextcloud
                - MYSQL_USER=nextcloud
                - MYSQL_PASSWORD=nextcloud_password
        redis:
            container_name: nextcloud-redis
            image: redis:7
            restart: unless-stopped
            cap_add:
                - SYS_RESOURCE
            user: "1000:1000"
            volumes:
                - ./redis:/data
            networks:
                - internal
    

    Start the containers:

    nc-up
    

    Further Optimizations

    Let’s optimize the installation a bit more. Starting fresh:

    nc-down
    nc-remove
    

    Update the Caddyfile:

    {
      # General Options
      log { 
        level ERROR
      } 
    
      # TLS Options
      auto_https disable_redirects
      email mail@deployn.de
      acme_ca https://acme-v02.api.letsencrypt.org/directory
    
      # Server Options
      servers { 
        protocols h1 h2 h2c
      } 
    }
    
    http://nextcloud.pixan.de {
      redir https://{host}{uri} permanent
    } 
    
    https://nextcloud.pixan.de {
    
      header {
        Strict-Transport-Security "max-age=15552000; includeSubDomains; preload"
        X-Forwarded-Proto "https"
        X-Forwarded-Host {host}
        X-Forwarded-For {remote}
        X-Real-IP {remote}
      } 
    
      redir /.well-known/carddav /remote.php/dav/ 301
      redir /.well-known/caldav /remote.php/dav/ 301
    
      # Respond Handlers
      reverse_proxy nextcloud:80
    }
    

    Inspect the proxy network:

    sudo docker network inspect proxy
    

    Update the docker-compose.yml file:

    networks:
        proxy:
            external: true
            name: proxy
        internal:
            external: false
    
    services:
        nextcloud:
            container_name: nextcloud
            image: nextcloud
            restart: always
            volumes:
                - ./data:/var/www/html/
            networks:
                - proxy
                - internal
            environment:
                - MYSQL_HOST=db
                - MYSQL_DATABASE=nextcloud
                - MYSQL_USER=nextcloud
                - MYSQL_PASSWORD=nextcloud_password
                - REDIS_HOST=redis
                - NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.pixan.de
                - PHP_UPLOAD_LIMIT=10G
                - OVERWRITEPROTOCOL=https
                - OVERWRITEHOST=nextcloud.pixan.de
                - TRUSTED_PROXIES=172.18.0.0/12
                - APACHE_DISABLE_REWRITE_IP=1
            depends_on:
                - db
                - redis
        cron:
            container_name: nextcloud-cron
            image: nextcloud
            restart: unless-stopped
            volumes:
                - ./data:/var/www/html/
            networks:
                - internal
            entrypoint: /cron.sh
            depends_on:
                - db
                - redis
        db:
            container_name: nextcloud-db
            image: mariadb:11.4
            restart: unless-stopped
            command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
            volumes:
                - ./db:/var/lib/mysql
            networks:
                - internal
            environment:
                - MYSQL_ROOT_PASSWORD=root_password
                - MYSQL_DATABASE=nextcloud
                - MYSQL_USER=nextcloud
                - MYSQL_PASSWORD=nextcloud_password
        redis:
            container_name: nextcloud-redis
            image: redis:7
            restart: unless-stopped
            cap_add:
                - SYS_RESOURCE
            user: "1000:1000"
            volumes:
                - ./redis:/data
            networks:
                - internal
    

    Access the Nextcloud container:

    sudo docker compose -f nextcloud/docker-compose.yml exec -u 33 nextcloud bash
    

    Run maintenance commands:

    php occ db:add-missing-indices
    php occ maintenance:repair --include-expensive
    php occ config:system:set maintenance_window_start --type=integer --value=1
    

    Nextcloud All in One

    Let’s try a different approach by using Nextcloud All-in-One, even without Caddy:

    nc-down
    nc-remove
    caddy-down
    

    Run the following command:

    sudo docker run \
    --init \
    --sig-proxy=false \
    --name nextcloud-aio-mastercontainer \
    --restart always \
    --publish 80:80 \
    --publish 8080:8080 \
    --publish 8443:8443 \
    --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
    --volume /var/run/docker.sock:/var/run/docker.sock:ro \
    nextcloud/all-in-one:latest
    

    Then, open https://your-vps-ip-address:8080 in your browser and configure Nextcloud.

    Conclusion

    Installing Nextcloud on a VPS is a great way to set up your own cloud. Using Docker and Caddy makes the installation simple and flexible. I hope this blog post helped you launch your own Nextcloud instance. If you have any questions or suggestions, feel free to leave a comment.


    This website uses cookies. These are necessary for the functionality of the website. You can find more information in the privacy policy