本文へスキップ
VPS とサーバー · 8 分

Nextcloud on a VPS: Docker, HTTPS and an Honest Look at Disk Space

A Nextcloud VPS gives you your own file sync, calendars, contacts and shared documents without a third-party cloud. With Docker Compose, MariaDB, Redis and Caddy for automatic HTTPS it runs in about 20 minutes. The catch is disk space: VPS plans have small NVMe disks, so use Nextcloud for sync and office work and keep bulk data elsewhere.

What you need

  • A VPS. Drive (2 vCPU, 4 GB RAM, 20 GB NVMe, $15/month) is a sensible start for a family or a small team. Pulse with 2 GB runs Nextcloud for one or two users, but with little headroom. If you add Nextcloud Office (Collabora), take Forge or Titan: the document server wants a gigabyte or more on its own.
  • A domain or subdomain (for example cloud.example.com) with an A record pointing to the server's IP.
  • Docker on Ubuntu 24.04 or Debian 12 (see Docker on a VPS).

About the disk

Titan, the largest VPS, has 30 GB, and the OS and Docker images take a few gigabytes of that. This is enough for the documents, calendars and contacts of a small team, but photo sync from phones fills it within months. It is not enough for a video archive or years of photos. For those, either connect external storage (step 6) or move to a dedicated server with terabytes of NVMe. Not sure which size fits? See Choosing a VPS plan.

1. Firewall and Docker

apt update && apt install -y ufw
ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw allow 443/udp
ufw enable
curl -fsSL https://get.docker.com | sh

2. Secrets

mkdir -p /opt/nextcloud && cd /opt/nextcloud
cat > .env <<EOF
DOMAIN=cloud.example.com
DB_PASSWORD=$(openssl rand -hex 16)
DB_ROOT_PASSWORD=$(openssl rand -hex 16)
ADMIN_PASSWORD=$(openssl rand -hex 12)
EOF
chmod 600 .env && cat .env

Write down ADMIN_PASSWORD: it is the password of the admin user.

3. docker-compose.yml

/opt/nextcloud/docker-compose.yml:

services:
  db:
    image: mariadb:11.4
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED
    environment:
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MARIADB_DATABASE: nextcloud
      MARIADB_USER: nextcloud
      MARIADB_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db:/var/lib/mysql

  redis:
    image: redis:7-alpine
    restart: unless-stopped

  app:
    image: nextcloud:stable
    restart: unless-stopped
    depends_on: [db, redis]
    environment:
      MYSQL_HOST: db
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: ${DB_PASSWORD}
      REDIS_HOST: redis
      NEXTCLOUD_ADMIN_USER: admin
      NEXTCLOUD_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      NEXTCLOUD_TRUSTED_DOMAINS: ${DOMAIN}
      OVERWRITEPROTOCOL: https
      OVERWRITECLIURL: https://${DOMAIN}
      TRUSTED_PROXIES: 172.16.0.0/12
      PHP_UPLOAD_LIMIT: 2G
      PHP_MEMORY_LIMIT: 512M
    volumes:
      - nextcloud:/var/www/html

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

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data

volumes:
  db:
  nextcloud:
  caddy_data:

4. Caddyfile

/opt/nextcloud/Caddyfile (replace the domain):

cloud.example.com {
    header Strict-Transport-Security "max-age=15552000"
    redir /.well-known/carddav /remote.php/dav 301
    redir /.well-known/caldav /remote.php/dav 301
    reverse_proxy app:80
}

5. Start

cd /opt/nextcloud
docker compose up -d
docker compose logs -f app
docker compose exec -u www-data app php occ background:cron

The first start installs Nextcloud, which takes a minute or two. Then log in at https://cloud.example.com as admin.

6. Keep the disk under control

docker compose exec -u www-data app php occ config:system:set trashbin_retention_obligation --value="auto, 30"
docker compose exec -u www-data app php occ config:system:set versions_retention_obligation --value="auto, 30"
docker compose exec -u www-data app php occ user:setting alice files quota "5 GB"
docker compose exec -u www-data app php occ app:enable files_external

Deleted files and old versions are kept for at most 30 days, and each user gets a quota. The External storage app lets you mount S3-compatible storage, SFTP or WebDAV from another machine as a folder, so bulk data never lands on the VPS disk. Check free space with df -h /.

Common mistakes

  • "Access through untrusted domain". You opened the server by IP or by another name. Use the domain from .env.
  • Large uploads fail. Raise PHP_UPLOAD_LIMIT and run docker compose up -d.
  • Skipping major versions. Nextcloud upgrades one major version at a time. Run docker compose pull && docker compose up -d regularly rather than once a year.
  • Backups are on you. docker compose exec -T db sh -c 'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" nextcloud' > nextcloud.sql plus the nextcloud volume, copied off the server.

Automating things around your cloud? See n8n on a VPS.

Order Drive

Nextcloud on a VPS: Docker, HTTPS and an Honest Look at Disk Space · dedisrv