BlogTutorialsSelf-Host Nextcloud on VPS: Your Own Cloud Storage (2025)

Self-Host Nextcloud on VPS: Your Own Cloud Storage (2025)

Adrian Silaghi
Adrian Silaghi
January 17, 2026
18 min read
5 views
#nextcloud #self-hosted #cloud storage #dropbox alternative #google drive alternative #vps #docker #file sync #privacy
Self-Host Nextcloud on VPS: Your Own Cloud Storage (2025)

Google Drive knows everything about your files. Dropbox can access your data. iCloud locks you into Apple's ecosystem. What if you could have all the convenience of cloud storage with complete privacy?

Nextcloud is the open-source answer. It's a full-featured cloud platform that you host yourself—file sync, calendar, contacts, video calls, collaborative documents, and much more. All running on your own server.

This guide walks you through setting up Nextcloud on a VPS, including performance optimization, mobile apps, and backup strategies.

Why Self-Host Nextcloud?

Feature Google Drive Dropbox Nextcloud
Cost (2TB) $10/mo $12/mo ~€9-15/mo (VPS+storage)
Data Ownership Google Dropbox 100% Yours
Privacy Scanned/indexed E2E optional Complete
Calendar/Contacts Yes No Yes
Collaborative Docs Google Docs Paper OnlyOffice/Collabora
Video Calls Meet No Talk
Extensibility Limited Limited 200+ apps

Requirements

Usage Users VPS Specs DanubeData Plan
Personal 1-3 2 vCPU, 4GB RAM Standard (€8.99/mo)
Family/Small Team 3-10 4 vCPU, 8GB RAM Performance (€17.99/mo)
Organization 10-50 4+ vCPU, 16GB RAM Dedicated plans

Storage options:

  • VPS local storage: Included in VPS price
  • S3 as primary storage: €3.99/TB/month (recommended for large libraries)

Step 1: Provision Your VPS

  1. Create a VPS on DanubeData
  2. Choose Ubuntu 24.04 LTS
  3. Select at least Standard plan (4GB RAM)
  4. Add extra storage if storing files locally

Step 2: Initial Server Setup

# SSH into your server
ssh root@YOUR_SERVER_IP

# Update system
apt update && apt upgrade -y

# Install Docker
curl -fsSL https://get.docker.com | sh

# Install Docker Compose
apt install -y docker-compose-plugin

# Configure firewall
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

# Set hostname
hostnamectl set-hostname nextcloud

Step 3: Configure DNS

# Add A record
Type: A
Name: cloud (or @ for root)
Value: YOUR_SERVER_IP
TTL: 300

# Verify
dig cloud.yourdomain.com +short

Step 4: Create Docker Compose Configuration

# Create directory
mkdir -p /opt/nextcloud
cd /opt/nextcloud

# Create docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: "3.8"

services:
  db:
    image: mariadb:11
    container_name: nextcloud-db
    restart: always
    command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
    volumes:
      - db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
    networks:
      - nextcloud

  redis:
    image: redis:7-alpine
    container_name: nextcloud-redis
    restart: always
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis:/data
    networks:
      - nextcloud

  app:
    image: nextcloud:29-apache
    container_name: nextcloud-app
    restart: always
    ports:
      - 8080:80
    volumes:
      - nextcloud:/var/www/html
      - data:/var/www/html/data
    environment:
      - MYSQL_HOST=db
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - REDIS_HOST=redis
      - REDIS_HOST_PASSWORD=${REDIS_PASSWORD}
      - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER}
      - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
      - NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_DOMAIN}
      - OVERWRITEPROTOCOL=https
      - OVERWRITEHOST=${NEXTCLOUD_DOMAIN}
      - PHP_MEMORY_LIMIT=1G
      - PHP_UPLOAD_LIMIT=16G
    depends_on:
      - db
      - redis
    networks:
      - nextcloud

  cron:
    image: nextcloud:29-apache
    container_name: nextcloud-cron
    restart: always
    volumes:
      - nextcloud:/var/www/html
      - data:/var/www/html/data
    entrypoint: /cron.sh
    depends_on:
      - app
    networks:
      - nextcloud

volumes:
  db:
  redis:
  nextcloud:
  data:

networks:
  nextcloud:
EOF

Create Environment File

# Generate passwords
MYSQL_ROOT_PASSWORD=$(openssl rand -hex 16)
MYSQL_PASSWORD=$(openssl rand -hex 16)
REDIS_PASSWORD=$(openssl rand -hex 16)
ADMIN_PASSWORD=$(openssl rand -hex 12)

# Create .env file
cat > .env << EOF
MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
MYSQL_PASSWORD=$MYSQL_PASSWORD
REDIS_PASSWORD=$REDIS_PASSWORD
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=$ADMIN_PASSWORD
NEXTCLOUD_DOMAIN=cloud.yourdomain.com
EOF

# IMPORTANT: Save these credentials!
echo "Admin username: admin"
echo "Admin password: $ADMIN_PASSWORD"
echo ""
echo "Save this information securely!"

# Secure the file
chmod 600 .env

Step 5: Set Up Reverse Proxy (Caddy)

# Install Caddy
apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install -y caddy

# Configure Caddy
cat > /etc/caddy/Caddyfile << 'EOF'
cloud.yourdomain.com {
    reverse_proxy localhost:8080

    # Required headers for Nextcloud
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
    }

    # Handle .well-known for CalDAV/CardDAV
    redir /.well-known/carddav /remote.php/dav 301
    redir /.well-known/caldav /remote.php/dav 301

    # Large file uploads
    request_body {
        max_size 16GB
    }

    encode gzip
}
EOF

# Reload Caddy
systemctl reload caddy

Step 6: Launch Nextcloud

cd /opt/nextcloud

# Start all services
docker compose up -d

# Watch logs for startup
docker compose logs -f app

# Wait for "ready to handle connections" message
# Initial setup takes 2-3 minutes

Step 7: Initial Configuration

  1. Visit https://cloud.yourdomain.com
  2. Log in with admin credentials from .env
  3. Complete the setup wizard

Essential Configuration

# Enter the Nextcloud container
docker exec -it nextcloud-app bash

# Run as www-data user
su -s /bin/bash www-data

# Set background jobs to cron (already handled by cron container)
php occ background:cron

# Set default phone region
php occ config:system:set default_phone_region --value="DE"

# Set maintenance window (for background tasks)
php occ config:system:set maintenance_window_start --type=integer --value=1

# Enable APCu for local caching
php occ config:system:set memcache.local --value="\OC\Memcache\APCu"

# Exit container
exit
exit

Step 8: Install Essential Apps

Access Admin Settings → Apps and install:

  • Calendar: CalDAV calendar sync
  • Contacts: CardDAV contact sync
  • Notes: Simple note-taking
  • Tasks: To-do lists
  • Talk: Video/audio calls
  • OnlyOffice or Collabora Online: Document editing
  • Photos: Photo gallery
  • Deck: Kanban boards

Step 9: Configure Desktop/Mobile Sync

Desktop Client

  1. Download from nextcloud.com/install
  2. Enter server URL: https://cloud.yourdomain.com
  3. Log in with your credentials
  4. Choose folders to sync

Mobile Apps

  1. Download "Nextcloud" from App Store / Play Store
  2. Enter server URL and credentials
  3. Enable auto-upload for photos (optional)

Calendar/Contacts Sync

# CalDAV URL for calendar apps:
https://cloud.yourdomain.com/remote.php/dav/calendars/USERNAME/

# CardDAV URL for contact apps:
https://cloud.yourdomain.com/remote.php/dav/addressbooks/users/USERNAME/

# Works with:
# - iOS (built-in)
# - macOS (built-in)
# - Thunderbird
# - Android (DAVx⁵ app)

Step 10: External Storage (S3)

For large storage needs, use S3 as your primary storage:

Enable External Storage App

  1. Admin Settings → Apps
  2. Enable "External storage support"

Configure S3 Storage

  1. Admin Settings → External storage
  2. Add new external storage:
Folder name: S3 Files
External storage: Amazon S3
Configuration:
  Bucket: your-nextcloud-bucket
  Hostname: s3.danubedata.com
  Port: 443
  Region: eu-central-1
  Enable SSL: Yes
  Enable Path Style: Yes
  Access key: your-access-key
  Secret key: your-secret-key

Use S3 as Primary Storage

# Add to config.php (inside container)
docker exec -it nextcloud-app bash

cat >> /var/www/html/config/config.php << 'EOF'
'objectstore' => [
    'class' => '\OC\Files\ObjectStore\S3',
    'arguments' => [
        'bucket' => 'nextcloud-primary',
        'hostname' => 's3.danubedata.com',
        'key' => 'your-access-key',
        'secret' => 'your-secret-key',
        'port' => 443,
        'use_ssl' => true,
        'region' => 'eu-central-1',
        'use_path_style' => true,
    ],
],
EOF

Step 11: Performance Tuning

PHP Configuration

# Already set in docker-compose, but verify:
docker exec nextcloud-app php -i | grep memory_limit
# Should show: memory_limit => 1G

# Check upload limits
docker exec nextcloud-app php -i | grep upload_max_filesize
# Should show: upload_max_filesize => 16G

Database Optimization

# Add indexes (improves performance significantly)
docker exec -it nextcloud-app bash
su -s /bin/bash www-data
php occ db:add-missing-indices
php occ db:convert-filecache-bigint
exit
exit

Preview Generation

# Install preview generator app
docker exec -u www-data nextcloud-app php occ app:install previewgenerator

# Generate previews for existing files
docker exec -u www-data nextcloud-app php occ preview:generate-all

# Add to cron for new files
# Already handled by cron container

Step 12: Backup Strategy

#!/bin/bash
# /opt/nextcloud/backup.sh

set -e

BACKUP_DIR="/opt/nextcloud/backups"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
S3_BUCKET="nextcloud-backups"

mkdir -p $BACKUP_DIR

echo "Starting Nextcloud backup: $DATE"

# Enable maintenance mode
docker exec -u www-data nextcloud-app php occ maintenance:mode --on

# Backup database
docker exec nextcloud-db mariadb-dump -u root -p$MYSQL_ROOT_PASSWORD nextcloud | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"

# Backup config
docker cp nextcloud-app:/var/www/html/config/config.php "$BACKUP_DIR/config_$DATE.php"

# Backup docker-compose and .env
cp /opt/nextcloud/docker-compose.yml "$BACKUP_DIR/docker-compose_$DATE.yml"
cp /opt/nextcloud/.env "$BACKUP_DIR/env_$DATE"

# Disable maintenance mode
docker exec -u www-data nextcloud-app php occ maintenance:mode --off

# Upload to S3
rclone copy "$BACKUP_DIR" danubedata:$S3_BUCKET/

# Clean up old local backups
find $BACKUP_DIR -type f -mtime +7 -delete

echo "Backup complete: $DATE"
# Make executable and schedule
chmod +x /opt/nextcloud/backup.sh

# Add to crontab (daily at 3 AM)
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/nextcloud/backup.sh >> /var/log/nextcloud-backup.log 2>&1") | crontab -

Step 13: Security Hardening

Enable Brute Force Protection

# Already enabled by default, verify:
docker exec -u www-data nextcloud-app php occ config:system:get auth.bruteforce.protection.enabled
# Should return: true

Two-Factor Authentication

  1. Install "Two-Factor TOTP" app
  2. Enable for your account in Personal Settings → Security
  3. Use with any authenticator app (Google Authenticator, Authy, etc.)

Fail2ban Integration

# Install fail2ban
apt install -y fail2ban

# Create Nextcloud filter
cat > /etc/fail2ban/filter.d/nextcloud.conf << 'EOF'
[Definition]
failregex = ^.*Login failed: '.*' (Remote IP: '').*$
            ^.*Trusted domain error: '.*' tried to access using .* as host.*(Remote IP: '').*$
ignoreregex =
EOF

# Create jail
cat > /etc/fail2ban/jail.d/nextcloud.local << 'EOF'
[nextcloud]
enabled = true
port = 80,443
protocol = tcp
filter = nextcloud
maxretry = 5
bantime = 3600
logpath = /var/lib/docker/volumes/nextcloud_nextcloud/_data/data/nextcloud.log
EOF

# Restart fail2ban
systemctl restart fail2ban

Updating Nextcloud

cd /opt/nextcloud

# Pull latest image
docker compose pull

# Stop containers
docker compose down

# Start with new version
docker compose up -d

# Run upgrade (if needed)
docker exec -u www-data nextcloud-app php occ upgrade

# Check status
docker exec -u www-data nextcloud-app php occ status

Cost Analysis

Storage Google Drive Dropbox Nextcloud (Self-Hosted)
100 GB $2/mo $12/mo €8.99/mo (VPS)
1 TB $10/mo $12/mo €8.99/mo (VPS)
2 TB $10/mo $20/mo €12.98/mo (VPS+S3)
5 TB $25/mo N/A €24.95/mo (VPS+S3)
+ Calendar/Contacts Included Not available Included
+ Unlimited Users Per user pricing Per user pricing Included

Get Started Today

Ready to own your cloud?

  1. Create a VPS on DanubeData (€8.99/mo)
  2. Follow this guide to deploy Nextcloud
  3. Install desktop/mobile apps
  4. Migrate your files from Google/Dropbox

Recommended Setup:

  • VPS: Standard €8.99/mo (4GB RAM) for personal use
  • S3 Storage: €3.99/mo + usage for large file storage
  • Total: ~€13/mo for a complete cloud replacement

👉 Create Your Nextcloud VPS

Need help setting up Nextcloud? Contact our team—we're happy to assist.

Share this article

Ready to Get Started?

Deploy your infrastructure in minutes with DanubeData's managed services.