Self-Hosting Nextcloud: AIO vs Docker Compose Compared - 夜莺博客

Self-Hosting Nextcloud: AIO vs Docker Compose Compared

Nextcloud is the closest thing the self-hosting world has to a drop-in replacement for Google Drive and Workspace: file sync, sharing, calendar, contacts, talk and collaborative document editing, all on hardware you own. The hard part is not installation — it is choosing between the two official paths, because they lead to very different operational lives. This guide compares the All-in-One container against a hand-built Compose stack, then walks the deployment and the post-install warnings that are genuinely worth fixing.

Path 1: Nextcloud All-in-One (AIO)

sudo docker run \
  --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

Browse to https://your-server-ip:8080, accept the self-signed warning, save the passphrase AIO prints (it is your recovery key), enter the domain, and click start. AIO pulls Nextcloud, the database, the cache and the backup containers and handles version upgrades and database migrations for you.

AIO caveats

  • Do not use the snap version of Docker. AIO manages child containers through the Docker socket and the snap confinement breaks it.
  • AIO bundles its own reverse proxy on 8080/8443 and expects to own the host. It does not want to live behind your existing proxy.
  • Backups and updates happen AIO's way; you give up granular control.

Path 2: A Compose Stack You Control

services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    volumes: ["./db:/var/lib/postgresql/data"]
    env_file: .env
    networks: ["nextcloud-internal"]
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}
    networks: ["nextcloud-internal"]
  app:
    image: nextcloud:30-fpm
    restart: unless-stopped
    volumes: ["./data:/var/www/html"]
    env_file: .env
    environment:
      - POSTGRES_HOST=db
      - REDIS_HOST=redis
      - REDIS_HOST_PORT=6379
    depends_on: ["db", "redis"]
    networks: ["nextcloud-internal"]
  web:
    image: nginx:alpine
    restart: unless-stopped
    ports: ["127.0.0.1:8080:80"]
    volumes:
      - ./data:/var/www/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    networks: ["nextcloud-internal"]
  cron:
    image: nextcloud:30-fpm
    restart: unless-stopped
    volumes: ["./data:/var/www/html"]
    entrypoint: /cron.sh
    depends_on: ["db", "redis"]
    networks: ["nextcloud-internal"]
networks:
  nextcloud-internal:
    driver: bridge

Note 127.0.0.1:8080: the container is deliberately not exposed publicly. TLS terminates in a host-level proxy.

POSTGRES_DB=nextcloud
POSTGRES_USER=nextcloud
POSTGRES_PASSWORD=$(openssl rand -base64 32)
REDIS_PASSWORD=$(openssl rand -base64 32)
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=$(openssl rand -base64 32)
NEXTCLOUD_TRUSTED_DOMAINS=cloud.example.com
chmod 600 .env
docker compose pull && docker compose up -d
docker compose logs -f app     # watch first-run setup, Ctrl-C when it settles
curl -I http://127.0.0.1:8080  # expect 302 -> /login

TLS and Trusted Proxies

docker compose exec -u www-data app php occ config:system:set overwriteprotocol --value="https"
docker compose exec -u www-data app php occ config:system:set trusted_proxies 0 --value="172.16.0.0/12"

Without overwriteprotocol behind a TLS-terminating proxy, Nextcloud generates http:// URLs and you get a redirect loop that looks like a broken login.

Fix the Warnings That Actually Matter

# Memory cache - Redis is up but Nextcloud does not know it yet
docker compose exec --user www-data app php occ config:system:set memcache.local --value '\OC\Memcache\APCu'
docker compose exec --user www-data app php occ config:system:set memcache.distributed --value '\OC\Memcache\Redis'
docker compose exec --user www-data app php occ config:system:set memcache.locking --value '\OC\Memcache\Redis'
docker compose exec --user www-data app php occ config:system:set redis host --value 'redis'

# Missing indexes after an upgrade
docker compose exec --user www-data app php occ db:add-missing-indices
docker compose exec --user www-data app php occ db:add-missing-columns
docker compose exec --user www-data app php occ db:add-missing-primary-keys

# Confirm background jobs run from the cron container, not AJAX
docker compose exec -u www-data app php occ background:cron

Add PHP_MEMORY_LIMIT=512M, PHP_UPLOAD_LIMIT=16G and APACHE_BODY_LIMIT=0 (or the FPM equivalents) so large uploads and office editing do not hit PHP's default ceilings, and raise client_max_body_size on the reverse proxy to match.

Storage Decision You Cannot Defer

Decide where uploads live before the first run. Moving data afterwards requires maintenance mode, occ maintenance:data-fingerprint and manual directory moves. Mount the data volume on the disk that will actually hold your files — and if that disk is a NAS or ZFS pool, size the dataset for snapshots before you start filling it.

Backups

# Database: portable dump
docker compose exec nextcloud-db pg_dump -U nextcloud nextcloud > nextcloud-db-backup.sql
# Data volume: stop, archive, start
docker compose stop
docker run --rm -v nextcloud-html:/data -v $(pwd):/backup alpine tar czf /backup/nextcloud-data.tar.gz /data
docker compose start

Test a restore. An untested backup is a belief, not a backup.

Related Reading

Deeper dives on the same topics from our archive:

原文链接:https://olivevps.com/blog/posts/self-host-nextcloud-vps.html