Tutorial

How to Install Nextcloud on a VPS

By Amith Kumar25 July 202614 min read
Self-hostingNextcloud
How to Install Nextcloud on a VPS
Verified 25 Jul 2026 · Ubuntu 24.04 LTS · nginx 1.24.0, MariaDB 10.11.14

What you will have

By the end of this guide you will install Nextcloud on a VPS from nothing: a bare Ubuntu 24.04 slice becomes your own private cloud, a genuine replacement for Google Drive that stores your files on a box you own, in a location you choose. Nextcloud gives you the files, calendar, contacts and photo sync you expect from a big platform, except the data lives on your slice and answers only to you.

That ownership is the reason to bother. For anyone in India who would rather keep customer records, invoices and family photos on Indian hardware than in someone else's data centre, the data-residency control this gives you is the whole point, and it is a growing consideration under the DPDP rules.

Let us start with the destination, so you know exactly what you are building toward. Every screenshot in this guide is real, captured while we stood up an actual instance on our lab box, so the version numbers and the interface you see below are the ones you will get, not a polished mock-up.

curl -sk https://your-domain/status.php
The finished install answering status.php with installed true, the exact version 34.0.2 and the Nextcloud product name, captured against the real lab instance cloud.boxlab.space over real HTTPS

That one response is Nextcloud telling you it is installed and live: the product name, the exact version, and installed: true. From there it is your dashboard, your files, your rules.

What this guide covers
  • Installing the stack Nextcloud needs: nginx, PHP-FPM, MariaDB and the right PHP extensions.
  • Creating a database and a least-privilege user scoped to Nextcloud alone.
  • Downloading Nextcloud, verifying the checksum, and placing it with a data directory outside the web root.
  • Serving it over a real HTTPS certificate and running the command-line installer.
  • Fixing every recommended-settings warning Nextcloud flags, until the admin overview is green.

Before you begin

This is a do-it-yourself guide, so it assumes a little groundwork. None of it is heavy.

What you need first
  • An Ubuntu 24.04 LTS slice you can log into over SSH as a user with sudo. Two vCPUs and 2 GB of RAM makes a comfortable personal cloud, and you can size up later.
  • A domain or subdomain you control, with access to edit its DNS records. Most people put Nextcloud on a subdomain like cloud.example.com.
  • The public IP of your slice, which you will point that one DNS record at.
  • Roughly forty minutes, and a habit of reading what each command reports back before running the next one.

Everything below is run as a normal sudo user over SSH. Where a step needs root it uses sudo, so you never log in as root directly, and where a step acts on the files Nextcloud owns it runs as the www-data web user.

Install the stack

Nextcloud is a PHP application, so it needs three things around it: a web server to accept requests, a PHP runtime to execute the code, and a database to hold the metadata. That is nginx plus PHP-FPM plus MariaDB, the same LEMP stack that runs most of the self-hosted web. One apt install brings all three, plus bzip2 for unpacking the release.

sudo apt update && sudo apt install -y nginx \
     php-fpm php-mysql mariadb-server bzip2
apt installing nginx, php-fpm, php-mysql and mariadb-server on Ubuntu 24.04, then the versions read back (nginx 1.24.0, PHP 8.3, MariaDB 10.11) and php -m confirming the Nextcloud PHP extensions gd, mbstring, curl, xml, zip, intl, bcmath, gmp, bz2, imagick and apcu are loaded

Read the versions back so you know what you are running rather than trusting the package name alone. On a current Ubuntu 24.04 slice this is nginx 1.24.0, PHP 8.3, and MariaDB 10.11. If you want to understand the PHP-FPM half of this stack in depth, our PHP-FPM and OPcache guide covers the runtime that Nextcloud leans on hardest.

Nextcloud also expects a specific set of PHP extensions: image handling, translations, the maths libraries its encryption uses, and the archive support its updater needs. Install them and PHP-FPM will pick them up.

sudo apt install -y php-gd php-mbstring php-curl php-xml \
     php-zip php-intl php-bcmath php-gmp php-bz2 php-imagick php-apcu

Confirm the extensions are loaded before moving on. A fresh install that skips one of these fails the setup checks later, so it is worth a look now.

php -m | grep -iE 'gd|mbstring|curl|xml|zip|intl|bcmath|gmp|bz2|imagick|apcu'
Checkpoint You should now have nginx serving its default page, PHP-FPM running behind a socket, MariaDB accepting local connections, and every extension above listed by php -m. If one is missing, install it and restart php8.3-fpm before you continue.

Create the database

A fresh MariaDB install ships with a few loose ends, so run sudo mysql_secure_installation first and answer yes to every prompt. Then create the database Nextcloud will live in, and a dedicated user that can touch that database and nothing else. This is the single most important database habit: Nextcloud never needs access to any schema but its own, so its user is granted rights on nextcloud.* only, never on *.*. If the app is ever compromised, the blast radius stops at this one database.

sudo mariadb
CREATE DATABASE nextcloud
  CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'ncuser'@'localhost'
  IDENTIFIED BY '<your-strong-db-password>';
GRANT ALL PRIVILEGES ON nextcloud.*
  TO 'ncuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Creating the utf8mb4 nextcloud database and a dedicated ncuser granted privileges on nextcloud.* only and never on all databases, the least-privilege database user, with the password masked to a placeholder

Pick a long, random password for ncuser and keep it somewhere safe. You will hand it to the installer in a moment and never type it again. The utf8mb4 character set is what lets Nextcloud store filenames and notes in every language, and emoji, without corruption.

Get Nextcloud

Download the current release straight from source, and do the one step most rushed installs skip: verify the checksum. The published .sha256 file lets you prove the archive arrived intact before you trust it.

cd /var/www
sudo curl -sSLO https://download.nextcloud.com/server/releases/latest.tar.bz2
sudo curl -sSLO https://download.nextcloud.com/server/releases/latest.tar.bz2.sha256
sha256sum -c --ignore-missing latest.tar.bz2.sha256
Downloading the current Nextcloud release, verifying it against the published checksum with latest.tar.bz2: OK, unpacking it, confirming version 34.0.2 from version.php, and creating a data directory outside the web root owned by www-data

A line reading latest.tar.bz2: OK means the download matches the checksum the Nextcloud project published. Now unpack it, create a data directory outside the web root, and give both to the web server user.

sudo tar -xjf latest.tar.bz2
sudo mkdir -p /var/www/nextcloud-data
sudo chown -R www-data:www-data /var/www/nextcloud /var/www/nextcloud-data

Keeping the data directory outside /var/www/nextcloud matters: it means your files can never be served directly by the web server even if a rule is misconfigured, because they do not live under the web root at all. The version.php file inside the unpacked folder confirms exactly which release you have, in our case Nextcloud 34.0.2.

Point your domain and turn on real HTTPS

Nextcloud is on the slice, but nothing answers your domain yet. In your DNS, add an A record for the hostname you want (a subdomain like cloud) pointing at your slice's public IP, and give it a few minutes to propagate.

Next, create the nginx server block. Nextcloud publishes a recommended nginx configuration, and it is worth using in full because it does real security work: it refuses to serve the data and config directories, routes every request through the front controller, sends the security headers Nextcloud expects, and serves the .mjs JavaScript modules the interface needs with the correct MIME type. The heart of it looks like this.

# /etc/nginx/sites-available/your-domain
server {
    listen 443 ssl http2;
    server_name your-domain;

    ssl_certificate     /etc/letsencrypt/live/your-domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain/privkey.pem;

    root /var/www/nextcloud;
    index index.php index.html /index.php$request_uri;
    client_max_body_size 512M;

    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;

    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }

    location / { try_files $uri $uri/ /index.php$request_uri; }

    location ~ \.php(?:$|/) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param HTTPS on;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    location ~ \.(?:css|js|mjs|woff2?|svg|gif|png|jpg|ico|wasm)$ {
        try_files $uri /index.php$request_uri;
        location ~ \.mjs$ { default_type text/javascript; }
        access_log off;
    }
}

The one line that trips up most first installs is the index directive: including /index.php$request_uri is what lets a request for a real directory like /apps/files/ fall through to the front controller instead of returning a 403. To get the free, auto-renewing certificate those ssl_certificate lines point at, install Certbot with sudo apt install -y certbot python3-certbot-nginx and run sudo certbot --nginx -d your-domain. Then enable the site and reload.

sudo ln -s /etc/nginx/sites-available/your-domain \
     /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx -t reporting the configuration syntax ok and test successful, then openssl confirming the served certificate is issued by Let's Encrypt and covers the domain through the wildcard, real HTTPS

A test is successful from nginx -t is the gate; nginx will not reload a broken config, so never skip it. With the certificate in place the padlock is real, issued by a public authority and trusted by every browser.

Install Nextcloud on a VPS with the setup wizard

You could finish the install by opening the site in a browser and filling in the web wizard. The command line is faster and more reliable, and it is the same tool you will use to run Nextcloud for years: occ. Run its maintenance:install and it creates the database tables, writes the config, and sets up your admin account in one pass.

cd /var/www/nextcloud
sudo -u www-data php occ maintenance:install \
     --database mysql --database-name nextcloud \
     --database-user ncuser --database-pass '<db-password>' \
     --admin-user ncadmin --admin-pass '<admin-password>' \
     --data-dir /var/www/nextcloud-data
occ maintenance:install creating the tables and the admin account and printing Nextcloud was successfully installed, with the database and admin passwords masked, then occ setting the domain as a trusted_domain
The genuine Nextcloud login page at cloud.boxlab.space served over real HTTPS with a valid padlock, framed in the ServerCake browser chrome, the sign-in form you reach the moment the install finishes

Nextcloud was successfully installed is the line you are waiting for. One more setting before you log in: tell Nextcloud which hostname is allowed to serve it, by adding your domain to trusted_domains. Nextcloud refuses to answer on any hostname not in this list, which is a deliberate defence against host-header attacks.

sudo -u www-data php occ config:system:set trusted_domains 1 \
     --value=your-domain

Open your domain in a browser now and you will land on the Nextcloud login page, served over your own certificate.

First login and the files UI

Log in with the admin user you just created. Nextcloud drops you into the Files app, pre-populated with a few sample documents and a Photos folder. This is the payoff: your own cloud drive, in your browser, over your own domain.

Before you trust it, confirm from the shell that the pieces line up: plain HTTP is pushed to HTTPS, your domain is trusted, and the admin account exists.

curl -skI http://your-domain/ | head -2
sudo -u www-data php occ config:system:get trusted_domains
sudo -u www-data php occ user:list
curl showing plain HTTP redirected 301 to HTTPS, occ listing the trusted domains including cloud.boxlab.space, and occ user:list confirming the ncadmin account exists
The real Nextcloud 34.0.2 Files app at cloud.boxlab.space after first login, showing the sample documents plus the genuinely uploaded Harvest-notes.md file in the list, 7 files and 3 folders, over real HTTPS

Now try the thing the whole install exists for. Drag a file into the browser window to upload it, and it appears in the list immediately, then click it to open it in place. The screenshots below show a real file we uploaded to the lab instance, Harvest-notes.md, sitting in the file list and then opened in the built-in viewer. Nothing left the slice; the file went straight into your data directory and Nextcloud served it back.

Checkpoint You should now see the Files app at your domain over HTTPS with a valid padlock, be able to upload a file and see it appear, and open it in the viewer. If the browser shows a "trusted domain" error instead, re-check the trusted_domains value against the hostname in your address bar.
The uploaded Harvest-notes.md file opened in the built-in Nextcloud text viewer at cloud.boxlab.space, showing its real content, the Q3 Nilgiri harvest notes, proof the file is genuinely stored and served back

A fresh Nextcloud runs, but its admin overview will flag a handful of recommended settings. These are not cosmetic: they are the difference between a sluggish, warning-covered instance and a fast, correct one. Open Settings, then Administration, then Overview to see them, and fix them from the shell.

First, give PHP room and add a memory cache. Nextcloud wants PHP's memory_limit at 512M, and it wants a local memory cache so it is not hitting the database for everything it can hold in RAM. APCu, which you installed earlier, is the simplest one.

grep '^memory_limit' /etc/php/8.3/fpm/php.ini
sudo -u www-data php occ config:system:set memcache.local \
     --value='\OC\Memcache\APCu'
sudo -u www-data php occ config:system:set default_phone_region --value=IN
sudo -u www-data php occ db:add-missing-indices
Fixing the recommended settings from the shell: PHP memory_limit raised to 512M, an APCu local memory cache configured, the default phone region set to IN, and the missing database indices added with occ db:add-missing-indices reporting Done

That block clears four separate warnings at once: the memory limit, the missing memory cache, the missing default phone region (Nextcloud needs one to parse phone numbers, and IN is India), and the database indices that speed up common queries. Set memory_limit = 512M in /etc/php/8.3/fpm/php.ini and restart php8.3-fpm so the new limit takes effect.

The other big one is background jobs. By default Nextcloud runs its housekeeping through the browser (AJAX), which is unreliable. Switch it to real system cron and let a timer run cron.php every five minutes.

sudo -u www-data php occ background:cron
sudo systemctl enable --now nextcloudcron.timer
sudo -u www-data php occ setupchecks
Switching Nextcloud background jobs to real system cron, enabling the systemd timer that runs cron.php every five minutes, and occ setupchecks reporting 58 checks green with no errors and no warnings
The genuine Nextcloud 34.0.2 admin overview at cloud.boxlab.space, the Security and setup warnings section with no errors and no warnings remaining after the recommended settings were applied, only optional informational notes, and the version panel showing 34.0.2 up to date

The setupchecks command runs the same checks the admin overview shows, from the shell. When it reports every line green, with no errors and no warnings, your instance is set up the way Nextcloud wants. The admin overview page then shows the same clean result, which is the last screenshot in this guide.

Do not skip this The background-jobs and memory-cache warnings are the two most common reasons a self-hosted Nextcloud feels slow. Fixing them costs three commands and changes how the instance feels day to day.

Keep it running and updated

Nextcloud ships fixes often, and occ is your control panel for staying current. occ status tells you the version and whether maintenance mode is on, and occ app:update --all keeps your installed apps patched. Nextcloud will also offer major-version upgrades through its built-in updater when they are ready for you.

sudo -u www-data php occ status
du -sh /var/www/nextcloud-data
sudo -u www-data php occ app:update --all
Keeping it running with occ: status showing installed 34.0.2 with maintenance off, the data directory size under /var/www/nextcloud-data where the files live, and occ app:update --all reporting all apps already up to date

Two habits keep a Nextcloud instance healthy for years. First, know where your data lives: the nextcloud-data directory holds every file your users upload, so that directory plus the database plus config.php is what a real backup must capture. Second, take those backups off the slice, because a backup that lives on the same machine is not a backup.

That is a complete, hand-built Nextcloud install: the stack, a least-privilege database, a verified download, your domain over real HTTPS, the installer, and every recommended setting fixed and green. Because you built every layer yourself, you can also fix every layer when something needs attention.

Two follow-ups turn this into a proper home cloud. The first is speed, a Redis cache plus PHP tuning, and our Redis guide is a good next read for that layer. The second is a backup and restore drill you have actually tested. And if you already run WordPress, this same slice will host it happily alongside Nextcloud, following our WordPress on a VPS tutorial.

Frequently asked questions

How big a VPS do I need to run Nextcloud? For a personal or small-family instance, two vCPUs and 2 GB of RAM is a comfortable starting point, which is why this guide targets roughly that. What grows the requirement is not Nextcloud itself but concurrent users, thumbnail generation for large photo libraries, and server-side apps like Collabora for document editing. Start modest, watch memory and response times in the admin overview, and resize the slice up only when the numbers say so. Storage is the resource you are most likely to add first, and on a VPS that is a straightforward volume attach.
Should I use MariaDB or PostgreSQL for Nextcloud? Both are fully supported and both run Nextcloud well, and this guide uses MariaDB because it is the most common choice and pairs naturally with the LEMP stack. PostgreSQL is an equally solid option and some administrators prefer it for larger instances. What matters far more than the engine is that you use a real database rather than the SQLite default, which Nextcloud only recommends for testing, and that you keep the database user scoped to the Nextcloud database alone.
Why keep the data directory outside the web root? Security in depth. If your files live under /var/www/nextcloud and a web server rule is ever misconfigured, those files could be served directly over HTTP. By putting the data directory at /var/www/nextcloud-data, outside the folder nginx serves, that whole class of mistake becomes impossible: the web server has no path to your files at all, and every download must go through Nextcloud's own access checks. It is a one-time decision at install that removes a permanent risk.
Is my data really private on a self-hosted Nextcloud? Yes, in the sense that matters: the files live on a slice you control, in a data centre you chose, under an account only you hold. No third party indexes your documents or mines them for advertising, and for anyone handling data under India's DPDP rules, keeping personal data on Indian infrastructure you operate is a meaningful compliance advantage. Privacy still depends on you doing the basics, a strong admin password, HTTPS as set up here, and regular updates, but the ownership question, who can reach your data, has a clear answer: only you.

Running Nextcloud on your own slice means you own the whole stack: the storage, the privacy posture, and the bill, with no per-seat pricing and no black box. To take it live, pick up a ServerCake slice, a clean Ubuntu 24.04 box hosted in India and billed in rupees with GST, and walk straight through the steps above on infrastructure that is yours end to end.


Run your own private cloud on a slice

Host Nextcloud on a slice

Your files should live on hardware you own, not a platform that mines them. Spin up a ServerCake slice, a clean Ubuntu 24.04 box in India billed in rupees with GST, and run Nextcloud exactly as in this tutorial, from the install to the files UI, with your data kept in India.

Get early access

This guide is provided for educational purposes and offered as is, without warranty of any kind. The commands change the configuration of a server you control, and some can lock you out if run out of order. Test on a non-production machine, keep a second session open, and take a snapshot or backup before you begin. You are responsible for changes made to your own systems; ServerCake accepts no liability for any loss, downtime, lockout, or damage arising from following this guide or running the accompanying script.

Spotted something out of date or have a question?

Built by the ServerCake team

Cloud that speaks India.

ServerCake is India's KYC-verified cloud: VMs and managed databases priced in ₹, billed with GST, on India-resident infrastructure. Reserve your spot for early access.

Reserve your spot