How to Install Nextcloud on a VPS with Docker: A Complete Guide
Your files are scattered across Google Drive, Dropbox, and three different USB drives you’ve misplaced. Sound familiar? Nextcloud fixes that. It’s a self-hosted cloud storage platform that gives you complete control over your data, a polished web interface, mobile apps, and a growing ecosystem of productivity tools, all running on infrastructure you own. No monthly storage fees. No terms-of-service surprises. No third-party eyes on your documents.
This guide walks you through deploying Nextcloud on a VPS using Docker and Docker Compose, with Nginx handling SSL termination via Let’s Encrypt. By the end, you’ll have a production-ready personal cloud accessible from anywhere, secured with HTTPS, and easy to maintain long-term.
What Is Nextcloud?
Nextcloud is an open-source file sync and sharing platform originally forked from ownCloud in 2016. Today it’s grown into something much bigger than a simple storage server. Think of it as your private Google Workspace: files, calendars, contacts, task lists, video calls, and a thriving app store, all under one roof that you control.
Organizations across healthcare, finance, and government use it precisely because sensitive data never leaves their own servers. For individuals, the appeal is simpler: you pay for the VPS once and store as much as your disk holds. Nextcloud scales from a single-user personal instance to enterprise deployments with thousands of accounts.
Prerequisites
Before touching a single command, make sure your environment meets these requirements.
| Requirement | Minimum | Recommended |
|---|---|---|
| OS | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
| CPU | 1 vCPU | 2 vCPUs |
| RAM | 2 GB | 4 GB |
| Disk | 20 GB SSD | 40 GB+ SSD |
| Docker Engine | 24.x | Latest stable |
| Docker Compose | v2.x | Latest stable |
| Domain name | Required | Required |
| Open ports | 80, 443 | 80, 443 |
You’ll also need a non-root sudo user on your VPS and a domain name with an A record already pointing to your server’s public IP. Let’s Encrypt won’t issue a certificate until DNS resolves correctly so get that sorted before you begin.
How to Install Nextcloud on a VPS with Docker
Step 1: Update Your System
Start fresh. A quick system update prevents dependency conflicts down the line and patches any outstanding security vulnerabilities.
sudo apt update && sudo apt upgrade -y
Reboot if the kernel was updated.
sudo reboot
Step 2: Install Docker Engine
Always install Docker Engine from the official Docker repository rather than Ubuntu’s default package manager. The Ubuntu-packaged version lags behind significantly and can cause compatibility issues with newer Compose features.
First, install the required dependencies and add Docker’s GPG key.
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
Next, add the Docker repository to your sources list.
echo /
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu /
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | /
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Now install Docker Engine and the Compose plugin.
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Add your user to the docker group so you don’t need sudo for every Docker command.
sudo usermod -aG docker $USER
newgrp docker
Confirm the installation works.
docker --version
docker compose version
Step 3: Configure the Firewall
Lock down your server. You only need SSH, HTTP, and HTTPS exposed to the public internet. Everything else stays shut.
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
Nextcloud’s application port never needs to be publicly accessible. Nginx will proxy traffic internally, so keep that port bound to localhost only.
Step 4: Create the Project Directory and Environment File
Keeping everything in one dedicated directory makes backups, updates, and troubleshooting far less painful.
mkdir -p ~/nextcloud && cd ~/nextcloud
Create a .env file to store your credentials. Never hardcode passwords directly in Compose files.
nano .env
Paste in the following, replacing each placeholder with your actual values.
# Database
MYSQL_ROOT_PASSWORD=your_strong_root_password
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
MYSQL_PASSWORD=your_strong_db_password
# Nextcloud admin account
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=your_strong_admin_password
# Your domain
NEXTCLOUD_TRUSTED_DOMAINS=cloud.yourdomain.com
Save and close the file. Use chmod 600 .env to restrict read access to your user only.
chmod 600 .env
Step 5: Write the Docker Compose File
This Compose file spins up three services: MariaDB as the database, Redis for caching and locking, and Nextcloud itself. Redis is not optional if you care about performance. Without it, file locking falls back to the database and things get sluggish fast under multiple simultaneous users.
nano docker-compose.yml
services:
db:
image: mariadb:11
container_name: nextcloud_db
restart: always
command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: nextcloud_redis
restart: always
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
app:
image: nextcloud:stable
container_name: nextcloud_app
restart: always
ports:
- "127.0.0.1:8080:80"
volumes:
- nextcloud_data:/var/www/html
environment:
MYSQL_HOST: db
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER}
NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_TRUSTED_DOMAINS}
REDIS_HOST: redis
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
volumes:
db_data:
nextcloud_data:
Notice that the app service binds to 127.0.0.1:8080 rather than 0.0.0.0:8080. That single distinction keeps Nextcloud off the public internet. Nginx handles all inbound traffic and forwards it internally.
Step 6: Install Nginx and Certbot
Nginx acts as the front door. It terminates SSL, forwards requests to the Nextcloud container, and handles the headers Nextcloud needs to function correctly behind a proxy.
sudo apt install -y nginx certbot python3-certbot-nginx
Step 7: Configure Nginx for Nextcloud
Create a dedicated server block for your domain. Replace cloud.yourdomain.com with your actual domain throughout.
sudo nano /etc/nginx/sites-available/nextcloud
server {
listen 80;
server_name cloud.yourdomain.com;
# Allow Let's Encrypt challenge
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name cloud.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/cloud.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cloud.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Required for large file uploads
client_max_body_size 10G;
client_body_timeout 300s;
client_body_buffer_size 512k;
# Security headers
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy no-referrer always;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebDAV and large file support
proxy_request_buffering off;
proxy_buffering off;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# Required for WebDAV clients
proxy_set_header Destination $http_destination;
}
# Nextcloud well-known redirects
location /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
}
Enable the site and verify the Nginx configuration syntax before reloading.
sudo ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 8: Obtain an SSL Certificate
With Nginx running and DNS pointing at your server, grab the certificate.
sudo certbot --nginx -d cloud.yourdomain.com
Certbot modifies your Nginx config automatically and sets up auto-renewal via a systemd timer. You can verify the renewal schedule with:
sudo systemctl status certbot.timer
Step 9: Launch Nextcloud
With the infrastructure in place, start all three containers.
cd ~/nextcloud
docker compose up -d
The first launch takes a minute or two. MariaDB initializes its data directory and Nextcloud runs its installation routine. Watch the logs to confirm everything starts cleanly.
docker compose logs -f app
Once you see Nextcloud reporting that configuration is complete, open your browser and navigate to https://cloud.yourdomain.com. You should land on the Nextcloud login screen. Sign in with the admin credentials you set in the .env file.
Step 10: Post-Installation Hardening
Nextcloud’s admin panel flags several common configuration issues under Settings > Administration > Overview. Run through these fixes to silence the warnings and tighten security.
First, set the default phone region inside the container via occ, Nextcloud’s command-line tool.
docker exec -u www-data nextcloud_app php occ config:system:set default_phone_region --value="US"
Enable the background job system to use cron rather than AJAX. Cron is more reliable for scheduled tasks like notifications and file indexing.
docker exec -u www-data nextcloud_app php occ background:cron
Set up a cron job on the host to trigger Nextcloud’s cron runner every five minutes.
crontab -e
Add this line at the bottom.
*/5 * * * * docker exec -u www-data nextcloud_app php /var/www/html/cron.php
Finally, run Nextcloud’s built-in maintenance scan to optimize the database.
docker exec -u www-data nextcloud_app php occ db:add-missing-indices
docker exec -u www-data nextcloud_app php occ db:convert-filecache-bigint
How to Update Nextcloud
Keeping Nextcloud current is straightforward with Docker. Pull the latest image, recreate the container, and Nextcloud handles the database migrations automatically on first boot.
cd ~/nextcloud
docker compose pull
docker compose up -d
After the update, check the admin overview page for any pending migration steps. Run occ upgrade manually if prompted.
docker exec -u www-data nextcloud_app php occ upgrade
Prune old images once you’ve confirmed the updated instance is healthy.
docker image prune -f
Troubleshooting Common Issues
Nextcloud Shows “Untrusted Domain” Error
This error appears when the domain in your browser doesn’t match what Nextcloud expects. It’s one of the most common first-run surprises. Check your .env file to confirm NEXTCLOUD_TRUSTED_DOMAINS matches exactly what you’re accessing in the browser, including subdomain.
You can also fix this on the fly using occ.
docker exec -u www-data nextcloud_app php occ config:system:set trusted_domains 1 --value="cloud.yourdomain.com"
Database Container Fails to Start
If MariaDB refuses to initialize, the most likely culprit is a leftover Docker volume from a previous failed attempt. Inspect the logs first.
docker compose logs db
If you see errors about an already-initialized data directory with mismatched credentials, tear down the stack and remove the volume entirely before starting fresh.
docker compose down -v
docker compose up -d
Warning: -v deletes all named volumes. Only use this on a fresh install with no data worth keeping.
Large File Uploads Fail or Time Out
Uploads failing on large files usually points to a size or timeout limit somewhere in the chain. Double-check these three settings.
- Nginx:
client_max_body_sizeshould be10Gor higher in your server block (already set in Step 7). - PHP inside the container: set
upload_max_filesizeandpost_max_sizevia environment variables or a custom PHP ini file. - Nextcloud itself: under Settings > Administration > Basic Settings, the maximum upload size is configurable.
You can override PHP limits by adding a custom ini file to the container volume at /var/www/html/ or by mounting one via Compose.
Nextcloud Redirects Loop or Returns 502
A 502 Bad Gateway typically means Nginx can’t reach the Nextcloud container. Verify the container is actually running.
docker compose ps
Then confirm Nginx is forwarding to the correct address and port. The proxy_pass directive must point to http://127.0.0.1:8080. Redirect loops almost always come from a missing or misconfigured X-Forwarded-Proto header. Confirm it’s present in your Nginx config and that Nextcloud’s overwriteprotocol is set to https.
docker exec -u www-data nextcloud_app php occ config:system:set overwriteprotocol --value="https"
Memories or Photos App Won’t Scan Files
Several Nextcloud apps require background jobs to process files. If file indexing seems stuck, trigger a manual scan.
docker exec -u www-data nextcloud_app php occ files:scan --all
If the cron job isn’t firing, revisit Step 10 and confirm the crontab entry exists for your user. You can also verify the last cron execution time inside the Nextcloud admin overview panel under Basic Settings.
What to Build Next
A working Nextcloud instance is just the foundation. Once you’re comfortable with the basics, here are some directions worth exploring.
- Nextcloud Talk: Enable the Talk app for end-to-end encrypted video calls and messaging directly inside your instance. You can add a TURN server for reliable NAT traversal.
- Nextcloud Office: Pair Nextcloud with a Collabora Online or OnlyOffice container to get real-time collaborative document editing, a genuine Google Docs alternative you fully control.
- External Storage: Mount S3-compatible object storage, SFTP endpoints, or even other WebDAV sources as folders inside Nextcloud via the External Storage app.
- Two-Factor Authentication: Install the Two-Factor TOTP app from the Nextcloud app store and enforce 2FA for all admin accounts at minimum.
- Automated Backups: Set up a cron job to dump the MariaDB database and archive the
nextcloud_dataDocker volume to an offsite location on a regular schedule.
Running your own cloud sounds like a weekend project. It is, and then it becomes something you wonder how you lived without. Nextcloud gives you genuine data sovereignty, a client app ecosystem that rivals the commercial giants, and the quiet satisfaction of knowing exactly where your files are at all times. Your VPS is already paying rent; put it to work.
The post How to Install Nextcloud on a VPS with Docker: A Complete Guide appeared first on GreenGeeks.

共有 0 条评论