How to Install Nextcloud on a VPS with Docker Compose

Build a private file cloud with PostgreSQL, Redis, HTTPS, background jobs and an off-site backup you have actually tested.

Choose a Cloud VPSOfficial Nextcloud Docker image
HYEHOST mascot installing Nextcloud with Docker on a VPS for private cloud storage

Nextcloud turns a server into a private file sync and collaboration platform. You control the domain, user accounts, storage location and update schedule instead of placing everything inside a third-party drive account. That control is useful, but it also makes you responsible for the database, HTTPS, background jobs and recovery plan.

This guide deploys the official community Docker image with PostgreSQL for metadata, Redis for transactional file locking, a dedicated cron container and a reverse proxy for TLS. The examples work on a current Ubuntu or Debian VPS with Docker Engine and the Compose plugin.

Plan the Nextcloud Docker Stack

A small personal server can start with 2 vCPU and 4GB RAM. More users, preview generation, office integrations and large media libraries need more headroom. Keep at least 10GB free outside the user-data allocation for container images, logs, database growth and upgrades.

ComponentPurposePublic exposure
Nextcloud ApacheWeb application and PHP runtimeLoopback only, behind proxy
PostgreSQL 16Users, shares, metadata and app stateDocker network only
Redis 7File locking and memory cacheDocker network only
Cron containerReliable background jobs every five minutesNone
Caddy or NginxHTTPS and public requestsPorts 80 and 443

Prepare the VPS

Install Docker using the steps in our Docker on Ubuntu guide, point a DNS record such as cloud.example.com at the VPS, then create a working directory:

sudo install -d -m 750 /opt/nextcloud
sudo chown "$USER":"$USER" /opt/nextcloud
cd /opt/nextcloud

Allow SSH, HTTP and HTTPS through the firewall. Do not expose PostgreSQL or Redis:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Create the Environment File

Generate unique values rather than reusing account passwords:

openssl rand -base64 36
openssl rand -base64 36
openssl rand -base64 24

Save /opt/nextcloud/.env and restrict it to your administrator account:

POSTGRES_DB=nextcloud
POSTGRES_USER=nextcloud
POSTGRES_PASSWORD=replace-with-a-long-random-password
NEXTCLOUD_ADMIN_USER=ncadmin
NEXTCLOUD_ADMIN_PASSWORD=replace-with-another-random-password
NEXTCLOUD_TRUSTED_DOMAINS=cloud.example.com
chmod 600 .env

Deploy Nextcloud with Docker Compose

Create compose.yml:

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

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --save 60 1 --loglevel warning
    volumes:
      - redis:/data

  app:
    image: nextcloud:apache
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - nextcloud:/var/www/html
    environment:
      POSTGRES_HOST: db
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      REDIS_HOST: redis
      NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER}
      NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
      NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_TRUSTED_DOMAINS}

  cron:
    image: nextcloud:apache
    restart: unless-stopped
    depends_on:
      - app
    entrypoint: /cron.sh
    volumes:
      - nextcloud:/var/www/html

volumes:
  db:
  redis:
  nextcloud:

Validate the file and start the stack:

docker compose config
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 app

The web container listens only on 127.0.0.1:8080. That prevents visitors from bypassing the HTTPS proxy by connecting directly to the container port.

Put Nextcloud Behind HTTPS

Caddy provides a compact host-level reverse proxy and obtains a certificate automatically once DNS points at the server:

cloud.example.com {
  encode zstd gzip
  reverse_proxy 127.0.0.1:8080
}

Reload Caddy, open the domain and confirm the certificate is valid. Nextcloud must know which proxy it can trust. Inspect the address or Docker bridge gateway that actually reaches the application, then add that exact value rather than trusting an unnecessarily broad private range:

docker network inspect nextcloud_default
docker compose exec --user www-data app php occ \
  config:system:set trusted_proxies 0 --value="172.18.0.1"
docker compose exec --user www-data app php occ \
  config:system:set overwriteprotocol --value="https"

Your bridge address may differ. The official Nextcloud reverse proxy documentation explains trusted proxies, forwarded headers and overwrite parameters in detail.

Finish the Nextcloud Configuration

Confirm that Redis is configured for locking, select Cron under Administration settings, and run the built-in checks:

docker compose exec --user www-data app php occ status
docker compose exec --user www-data app php occ config:list system
docker compose exec --user www-data app php occ maintenance:repair

The cron sidecar executes Nextcloud's cron.php on the schedule supplied by the official image. It is more reliable than AJAX background jobs, which only run when users visit the site.

  • Enable multi-factor authentication for administrator accounts.
  • Keep the database and Redis private to the Docker network.
  • Install only the Nextcloud apps you actively use.
  • Set upload and PHP limits deliberately for the largest expected file.
  • Monitor disk space, container health, certificate renewal and backup age.
  • Apply host security updates and remove unused services.

Back Up the Database, Configuration and Files Together

A Nextcloud recovery needs three parts: the PostgreSQL database, the complete Nextcloud volume and the secrets or Compose configuration required to recreate the stack. Put Nextcloud into maintenance mode briefly so the file and database copies represent the same point in time:

mkdir -p backups
docker compose exec --user www-data app php occ maintenance:mode --on
docker compose exec -T db pg_dump -U nextcloud nextcloud \
  | gzip > backups/nextcloud-db-$(date +%F).sql.gz
docker volume ls | grep nextcloud
docker run --rm \
  -v nextcloud_nextcloud:/source:ro \
  -v "$PWD/backups":/backup \
  alpine tar czf /backup/nextcloud-files-$(date +%F).tar.gz -C /source .
docker compose exec --user www-data app php occ maintenance:mode --off

Compose may use a different project prefix for the volume. Confirm it with docker volume ls before running the archive command. Copy the database dump, volume archive, compose.yml and encrypted secret material to an independent destination. Our rclone Storage Box guide covers encrypted SFTP transfers and restore testing.

Test a Clean Restore

Do not wait for an outage to discover a missing volume or password. Build an isolated test stack, restore the database and file archive, then verify logins, shared links, file previews and a selection of downloads. Keep the test domain private so it cannot send notifications or conflict with production clients.

Record the exact restore commands and the time required. That turns a collection of backup files into an actual recovery procedure.

Update Nextcloud Without Guesswork

Read the release notes and do not skip unsupported major-version jumps. Before an update, confirm the backup and free disk space, then pull and recreate the containers:

docker compose exec --user www-data app php occ status
docker compose pull
docker compose up -d
docker compose exec --user www-data app php occ upgrade
docker compose exec --user www-data app php occ status

Pin a major image tag when you need a controlled upgrade window. A floating image tag is convenient, but it should not replace release review and a tested rollback plan.

Frequently Asked Questions

What size VPS do I need for Nextcloud?

Start around 2 vCPU and 4GB RAM for a small personal or family installation, then size storage for the live library plus database, previews and update headroom. More users and apps need more memory and CPU.

Can I use a Storage Box as the primary Nextcloud data directory?

Use local attached storage or a Storage VPS for primary data. An SFTP or WebDAV Storage Box is better as an independent backup destination than as the latency-sensitive application filesystem.

Does Nextcloud need Redis?

It can run without Redis on a tiny instance, but Redis is recommended for transactional file locking and caching on a real multi-user server.

Does the Docker image include HTTPS?

No. The application container serves HTTP and a reverse proxy such as Caddy or Nginx terminates TLS. Configure trusted domains and proxies carefully.

How should I back up Nextcloud?

Back up PostgreSQL, the complete Nextcloud volume, Compose configuration and protected secrets as one recovery set. Keep an independent off-site copy and prove it with a clean restore.

Is Nextcloud a Google Drive alternative?

Yes, for self-hosted file sync, sharing and collaboration. The trade-off is that you operate the security, updates, monitoring and recovery process yourself.