DEV Community

overnight.host
overnight.host

Posted on Originally published at overnight.host

Self-hosting Nextcloud properly: storage, cron, and the settings everyone gets wrong

Nextcloud installs in ten minutes and then spends two years being mysteriously slow. Almost all of it comes down to four settings and one decision about where the files live.

Decide the shape first

There are three sane ways to run it, and picking the wrong one is the expensive mistake.

  • Nextcloud AIO — an all-in-one master container that manages the rest for you. Good defaults, handles backups and updates, and it wants the Docker socket and a port of its own. Easiest if you want opinions rather than knobs.
  • Plain Docker Compose — Nextcloud, a database and Redis as three services you own. More work, total control, and the shape used below.
  • A managed instance — someone else's problem entirely. Fine choice if the point was the files, not the server.

What is not a sane way: SQLite. Nextcloud will let you, and it works right up until two devices sync at once.

Sizing: it is a disk problem, not a CPU problem

Nextcloud is a file server with a web UI. CPU matters for thumbnail generation and the occasional big scan; the thing that actually runs out is disk.

Budget for what you will store in two years, not what you have today, and remember the multiplier: a file that exists on your laptop, your phone and the server exists three times, and versioning keeps old copies of anything you edit. 2 GiB of RAM is a comfortable floor for a small household instance with Redis alongside; the database is small unless you have hundreds of thousands of files.

If the disk is the constraint, plan how you grow it before you fill it — add extra storage to your VPS covers the mechanics on our machines.

The compose file

services:
  db:
    image: mariadb:11.4
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
    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:30-apache
    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_TRUSTED_DOMAINS=cloud.example.com
      - OVERWRITEPROTOCOL=https
      - PHP_MEMORY_LIMIT=512M
      - PHP_UPLOAD_LIMIT=10G
    volumes:
      - nextcloud:/var/www/html
    ports:
      - "127.0.0.1:8080:80"

  cron:
    image: nextcloud:30-apache
    restart: unless-stopped
    depends_on: [db, redis]
    volumes:
      - nextcloud:/var/www/html
    entrypoint: /cron.sh

volumes:
  db:
  nextcloud:
Enter fullscreen mode Exit fullscreen mode

The fourth service is not optional, and it is the one most guides leave out. Read on.

The four settings everyone gets wrong

1. trusted_domains. Nextcloud refuses any hostname it was not told about, with a blunt "access through untrusted domain" page. Set it at install (NEXTCLOUD_TRUSTED_DOMAINS above) or fix it later in config/config.php. Add every name you will really use, including the one you use from inside your own network.

2. overwriteprotocol. Behind a reverse proxy that terminates TLS, PHP sees plain HTTP and starts generating http:// links inside an https:// page. Browsers block them and the UI half-breaks. OVERWRITEPROTOCOL=https fixes it. If your proxy is on a different host, set trusted_proxies too, or every login shows up in the logs from the proxy's address instead of the user's.

3. Background jobs. A default install runs its background jobs via AJAX, meaning "when somebody happens to load a page". File scans, previews, cleanup and notifications then run late or never, and the instance feels slower every month. The cron service above is the fix: the same image with /cron.sh as the entrypoint, sharing the volume. Then set the method to Cron in Administration → Basic settings.

4. Upload limits. They are enforced in three places and the smallest wins: PHP (PHP_UPLOAD_LIMIT), the reverse proxy body size, and any intermediate CDN. Raising one and forgetting the others is why "it fails at exactly 512 MB".

HTTPS and the proxy

cloud.example.com {
    reverse_proxy 127.0.0.1:8080
    request_body {
        max_size 10GB
    }
    header {
        Strict-Transport-Security "max-age=31536000;"
    }
    redir /.well-known/carddav /remote.php/dav 301
    redir /.well-known/caldav /remote.php/dav 301
}
Enter fullscreen mode Exit fullscreen mode

Those two redirects are what silence the "your web server is not properly set up to resolve /.well-known/caldav" warning, and without them contact and calendar sync in some clients simply does not find the server. Point the DNS record first — point your domain at your service if you have not done that before.

After the install: run the checks

The admin overview page has a security & setup warnings section. Do not treat it as decoration; it is a genuine checklist. The two that matter most on a fresh install:

docker compose exec -u www-data app php occ db:add-missing-indices
docker compose exec -u www-data app php occ db:add-missing-primary-keys
Enter fullscreen mode Exit fullscreen mode

Nextcloud ships new indices with new versions but will not add them to a live database on its own, and a table without them gets slow in a way that looks like "the server is underpowered".

Backups

Three things, and you need all three:

  1. the databasemysqldump (or mariadb-dump) of the nextcloud database;
  2. the data directory — the files themselves, which is the big one;
  3. config/config.php — small, and irritating to reconstruct.

Put the instance into maintenance mode first so the dump and the files agree with each other:

docker compose exec -u www-data app php occ maintenance:mode --on
# dump the database, copy the data directory
docker compose exec -u www-data app php occ maintenance:mode --off
Enter fullscreen mode Exit fullscreen mode

Copy the result off the machine. Nothing about self-hosting includes a backup by default — that part is yours to arrange, on any provider. back up your VPS is our own write-up of what that means in practice.

On overnight.host

Full disclosure: this is what we sell. If you want the app without the sysadmin, we run Nextcloud as a managed container with its own volume and hostname — you get the app and a URL, not a root shell.

One-click apps — EUR 4 to EUR 12 a month, hosted in Germany (EU). Eight apps: n8n, Uptime Kuma, Vaultwarden, Gitea, Nextcloud, Ghost, Managed WordPress, Private AI Chat. Each customer gets an isolated Docker network and volume, plus a hostname under apps.overnight.host on a real wildcard certificate. Memory and CPU are capped per plan by the container runtime.

You order in the shop, pay by card (Stripe) or SEPA bank transfer, and your login details are e-mailed to you once the service is set up. Support is e-mail, run by one person, with no guaranteed response time. All prices are final totals under the German small-business rule (§19 UStG); no VAT is added or shown.

Order one-click-nextcloud → · One-click apps overview

FAQ

How much disk does Nextcloud need?

As much as the files, plus room for file versions and the trash, plus the previews it generates. Size for what you expect in two years rather than what you have now, and check the versioning and retention settings early — they are the quiet consumers.

Can I use SQLite?

Only for a look-around. Nextcloud supports it, but it serialises writes and falls over as soon as two clients sync at the same time. MariaDB or PostgreSQL for anything you intend to keep.

Why are my uploads failing at a fixed size?

Something in the chain has a body-size limit smaller than the file. Check PHP's upload and post limits, the reverse proxy's max body size, and any CDN in front. The smallest number wins, and it is usually the proxy.

Do I need Redis?

Strongly recommended. Nextcloud uses it for transactional file locking and caching; without it you eventually meet the "file is locked" errors under concurrent sync. It costs a few megabytes of RAM.

Is a managed Nextcloud different from self-hosting?

Functionally it is the same application. The difference is who holds the root shell: with a managed container you get the app, a hostname and a certificate, and you do not get to install PHP extensions or run occ yourself. If you need that control, put it on a VPS instead.


Written by the person who runs overnight.host: a small, honest hosting company on dedicated bare metal — Linux VPS, game servers, web hosting. Live status at up.overnight.host.


Originally published at overnight.host — the canonical, kept-current version of this guide.

Top comments (0)