Build Your Own Google Drive: A Practical Nextcloud Setup on Linux

Files, calendars and contacts under your roof

Contents

Cloud storage is wonderfully convenient right up until you read the fine print. Your files sync everywhere, your photos back themselves up, your calendar follows you between devices, and in exchange a very large company gets a detailed map of your life and the right to change the terms whenever it likes. The number that finally pushed me was 2 terabytes: that’s what my household photo library, document archive, and assorted detritus had grown to, and renting that much storage from a big provider in perpetuity, forever, with my data as the collateral, stopped feeling like a deal. Nextcloud is the open-source answer to that bargain: a self-hosted platform that gives you file sync, calendars, contacts, and even office documents, all running on a Linux box you control. This guide gets a robust Nextcloud running with Docker, puts it behind HTTPS, connects your devices, and sets sensible expectations about how it compares to the polished giants.

Running your own cloud is part of a broader move toward owning the boring, load-bearing parts of your digital life — the same instinct that drives a self-hosted Jellyfin media server instead of yet another streaming subscription.

What Nextcloud Actually Is

Advertisement

Nextcloud is a file sync and share platform, the spiritual successor to the old ownCloud project from which it forked in 2016 when most of the core developers, led by Frank Karlitschek, walked away to start it. But describing it as “self-hosted Dropbox” undersells it. Out of the box it handles file storage and sharing, and through its app ecosystem it grows into a calendar server, a contacts server, a collaborative document editor, a photo gallery, a notes app, and more.

The core appeal is consolidation. Instead of one service for files, another for calendars, and a third for contacts, Nextcloud offers a single account that ties them together with a coherent permissions model. You log in once, and your files, events, and address book all live in the same place, synced to your laptop and phone through standard protocols rather than proprietary lock-in.

Crucially, those protocols are open. Files sync over WebDAV, calendars over CalDAV, and contacts over CardDAV, which means you are not trapped: any compliant client can talk to your server, today or in a decade.

Installing With Docker Compose

Nextcloud runs best with a proper database and a cache, so we will define three services together: the application, a PostgreSQL database, and Redis for caching and file locking. Create a working directory and a compose.yaml:

1
mkdir -p ~/nextcloud && cd ~/nextcloud
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    volumes:
      - ./db:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${DB_PASSWORD}

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

  app:
    image: nextcloud:apache
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - ./html:/var/www/html
    environment:
      POSTGRES_HOST: db
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      REDIS_HOST: redis
      NEXTCLOUD_TRUSTED_DOMAINS: cloud.example.com
    depends_on:
      - db
      - redis

Put your database password in a .env file alongside it:

1
2
# .env
DB_PASSWORD=use-a-long-random-password-here

Then start the stack and watch it initialise:

1
2
docker compose up -d
docker compose logs -f app

As with any sensitive service, the app port is bound to 127.0.0.1 so that only your reverse proxy can reach it. The first time the container starts it sets up the database schema, which can take a minute.

Why PostgreSQL rather than the MySQL/MariaDB that older guides default to? Both work, but Nextcloud’s larger installs behave more predictably on PostgreSQL, and the Alpine image is small and updates cleanly. Why Redis as a separate service rather than letting Nextcloud cache in PHP’s local memory? Because PHP’s APCu cache is per-process and does not handle file locking across requests, which is exactly the situation that produces the dreaded “file is locked” errors when two clients sync the same file at once. Redis gives you a shared cache and a proper transactional lock store, and wiring it in from day one saves you a confusing afternoon later.

HTTPS via a Reverse Proxy

Advertisement

Nextcloud handles real data and login credentials, so it must be served over HTTPS. A reverse proxy terminates TLS and forwards to the loopback port. Caddy keeps this refreshingly short:

1
2
3
cloud.example.com {
    reverse_proxy 127.0.0.1:8080
}

Caddy fetches and renews a Let’s Encrypt certificate automatically. Whichever proxy you use, set the overwriteprotocol correctly so Nextcloud knows it is being served over HTTPS. With the Apache image, the common fix is to tell Nextcloud about the proxy by editing config/config.php inside the html volume, adding your proxy’s address to trusted_proxies and setting 'overwriteprotocol' => 'https'. Without this, you may see mixed-content warnings or broken links.

Once the certificate is live, open https://cloud.example.com, create the admin account when prompted, and you have a working cloud.

Syncing Desktop and Mobile

A cloud you cannot reach from your devices is just an expensive folder. Nextcloud provides first-party clients for every platform.

On the desktop, install the Nextcloud client on Linux, macOS, or Windows. It works like the Dropbox client: you point it at your server URL, authenticate, and choose which folders to sync. Virtual file support means you can browse your entire library while only downloading files when you open them, which is handy on a laptop with a small disk.

On mobile, the official Android and iOS apps handle file access and, importantly, automatic photo upload. Point the camera-upload feature at a folder and every photo you take backs itself up to your own server, which for many people is the single feature that justifies the whole project.

Calendars and Contacts via CalDAV and CardDAV

Files are only half the story. Enable the Calendar and Contacts apps from the Nextcloud app store, and your server becomes a personal information manager.

These sync over the open CalDAV and CardDAV standards, so you connect them with standard clients rather than a bespoke app. On Android, the DAVx5 app bridges Nextcloud to the system calendar and contacts so they appear natively. On iOS and macOS, CalDAV and CardDAV accounts are supported directly in Settings. On the desktop, Thunderbird and many other clients speak both protocols.

The practical upshot is that your appointments and address book live on your hardware, sync across every device, and never get mined for advertising. The connection URLs are discoverable from the Calendar and Contacts apps in the web interface.

The Apps Ecosystem

What turns Nextcloud from a file server into a platform is its app store, reachable from the admin menu. A few worth knowing:

  • Nextcloud Office, backed by Collabora, gives you in-browser editing of documents, spreadsheets, and presentations with real-time collaboration.
  • Notes offers a clean Markdown notebook synced across devices.
  • Talk provides text, voice, and video chat without a third party in the middle.
  • Memories or Photos turn your uploaded images into a proper gallery with timelines and albums.

A word of restraint: every app adds load and maintenance. Install what you will genuinely use, not the entire catalogue, because a lean Nextcloud is a fast and reliable one.

Performance Tuning and Maintenance

Nextcloud has a reputation for feeling sluggish, and almost always that reputation comes from a default install that was never tuned. A few changes make a large difference.

First, give PHP more memory. The default 512 MB is tight for a busy instance; raising the PHP_MEMORY_LIMIT environment variable to 1G or more smooths things out. Second, set up background jobs properly. Nextcloud needs to run housekeeping tasks, and the AJAX method that runs them on page load is the slowest option. Switch to system cron so the work happens reliably in the background:

1
docker compose exec -u www-data app php occ background:job:mode cron

Then schedule the cron container or a host cron entry to run cron.php every five minutes. Redis, which we already wired in, handles memory caching and transactional file locking, eliminating a whole class of “file is locked” errors. The admin overview page flags configuration warnings; work through them and most performance complaints evaporate.

Third, enable the database indices and missing primary keys that the overview page nags about. These are one-off occ commands that Nextcloud will print for you:

1
2
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

The difference on a library of tens of thousands of files is not subtle — file listings that took seconds drop to instant. People who declare Nextcloud “slow” have almost universally skipped this step.

Troubleshooting the Things That Actually Break

Nextcloud has a handful of failure modes that everyone hits, and knowing them in advance turns a lost evening into a thirty-second fix.

“Access through untrusted domain.” You opened the site on a hostname Nextcloud doesn’t recognise. Add it to the trusted-domains array:

1
2
docker compose exec -u www-data app php occ config:system:set \
    trusted_domains 1 --value=cloud.example.com

Mixed-content warnings or login loops behind the proxy. Nextcloud thinks it’s being served over plain HTTP because the proxy is terminating TLS upstream. Tell it the truth by setting 'overwriteprotocol' => 'https' and adding the proxy’s loopback address to trusted_proxies in config/config.php. This is the single most common reverse-proxy problem and the symptoms are wildly misleading until you know the cause.

Uploads of large files fail partway. The defaults cap upload size and PHP execution time. Raise PHP_UPLOAD_LIMIT in the compose environment and, if your proxy is the bottleneck, lift client_max_body_size (nginx) or the equivalent. Caddy has no such limit by default, which is one more reason I reach for it.

Sync client says “file locked” repeatedly. Either Redis isn’t actually connected, or a previous crash left stale locks. Confirm Redis is reachable from the app container, then clear the locks:

1
docker compose exec -u www-data app php occ maintenance:repair

The overview page warns about a missing .well-known redirect. CalDAV and CardDAV autodiscovery rely on it. The Apache image handles this internally, but if you front it with your own web server you need to add the .well-known/caldav and .well-known/carddav redirects to your proxy config, or device setup turns into a hostname-guessing game.

For anything network-adjacent — clients that can resolve the server from the LAN but not over the VPN, or vice versa — the usual culprit is DNS, and the debugging approach is the same one I use everywhere; running your own resolver, as in DNS over HTTPS at home, removes a surprising amount of this guesswork.

Hardening Before You Open It Up

A Nextcloud holding your real data on a real domain is a target, so spend ten minutes on the basics. Enable two-factor authentication for every account from the security settings — the TOTP app is one click to install. Turn on brute-force protection (it’s on by default in current versions; confirm it). Keep the app and its containers updated, because Nextcloud ships security fixes regularly and an unpatched instance facing the internet is asking for trouble. If you don’t strictly need it reachable from the public internet, don’t expose it at all — put it behind a VPN and reach it from there, which is both simpler and dramatically safer than fronting it to the whole world.

Backups and Realistic Expectations

Your important data lives in three places: the db directory, the html directory, and any external storage you have mounted. Back all of them up consistently. For a clean database snapshot, dump it rather than copying live files:

1
docker compose exec -u postgres db pg_dump nextcloud > /backups/nextcloud-$(date +%F).sql

Pair that with an archive of the html data directory, store the result off the machine, and test a restore at least once.

Finally, the honest comparison. Nextcloud gives you ownership, privacy, and a genuinely capable feature set, and for files, calendars, and contacts it is excellent. What it does not give you is Google’s planet-spanning reliability, instant global sync, or the army of engineers keeping the lights on. Search across millions of files will not feel as instant, and you are responsible for uptime and updates. For most self-hosters that trade is well worth making. You are swapping a little convenience for a lot of control, and ending up with a cloud that is unmistakably yours.

Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.