How to Set Up Nextcloud on a Dedicated Server (Ubuntu 24.04 LTS)

Nextcloud is a self-hosted file sync, share, and collaboration platform — a private alternative to Dropbox or Google Drive that you fully control. This guide walks through a production-ready manual installation on a dedicated Ubuntu 24.04 LTS (Noble Numbat) server, using Nginx, PHP-FPM, MariaDB, Redis, and a Let's Encrypt certificate.

This is the recommended path when you need real control over PHP tuning, caching, and the database engine, rather than the Snap or Docker AIO shortcuts.

What You'll Need

  • A dedicated or VPS server running Ubuntu 24.04 LTS, with root or sudo access
  • At least 2 vCPUs and 4 GB RAM for a small team; 8 GB+ RAM is recommended if you expect dozens of concurrent users or plan to run Collabora/OnlyOffice document editing alongside Nextcloud
  • SSD or NVMe storage — Nextcloud's database performance depends heavily on disk I/O
  • A domain or subdomain (e.g. cloud.yourdomain.com) with an A/AAAA record pointing to the server's public IP
  • Basic comfort with the Linux command line

Nextcloud's official documentation currently lists PHP 8.2, 8.3, 8.4, and 8.5 as supported for the current Nextcloud 34 release line, with PHP 8.3 as a solid recommended baseline — and that's exactly what Ubuntu 24.04 ships in its default repositories, so no third-party PPA is required for PHP itself.

Step 1: Update the System and Set the Hostname

Bash
sudo apt update && sudo apt -y upgrade
sudo hostnamectl set-hostname cloud
sudo apt -y install curl wget unzip software-properties-common gnupg2 ca-certificates apt-transport-https

Reboot if the kernel was updated:

Bash
sudo reboot

Step 2: Configure the Firewall

Bash
sudo apt -y install ufw
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

Step 3: Install MariaDB

Ubuntu 24.04's default repositories ship MariaDB 10.11, which falls within Nextcloud's recommended MariaDB range (Nextcloud currently recommends MariaDB in the 10.6–11.4 window, with newer 11.x releases sometimes flagged with a compatibility warning during setup). Installing from Ubuntu's own repos avoids that friction entirely.

Bash
sudo apt -y install mariadb-server mariadb-client
sudo mysql_secure_installation

Answer the prompts: set a strong root password, remove anonymous users, disallow remote root login, and remove the test database.

Create the Nextcloud database and a dedicated user:

SQL
sudo mysql -u root -p

CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
CREATE USER 'ncadmin'@'localhost' IDENTIFIED BY 'REPLACE_WITH_A_STRONG_PASSWORD';
GRANT ALL PRIVILEGES ON nextcloud.* TO 'ncadmin'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Then set the required transaction isolation level and binary log format, which Nextcloud's system requirements call for on MySQL/MariaDB:

Bash
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

Under the [mysqld] section add:

INI
transaction-isolation = READ-COMMITTED
binlog_format = ROW

Restart MariaDB:

Bash
sudo systemctl restart mariadb

Step 4: Install PHP 8.3 and Required Extensions

Bash
sudo apt -y install php-fpm php-cli php-common php-mysql php-gd php-curl \
  php-mbstring php-intl php-imagick php-xml php-zip php-bcmath php-gmp \
  php-apcu php-redis php-ldap php-smbclient php-ssh2 php-imap

Verify the version installed matches what Nextcloud expects:

Bash
php -v

You should see PHP 8.3.x, which sits comfortably inside Nextcloud's currently supported range.

Edit the PHP-FPM pool configuration for tuning (adjust paths for your PHP version, e.g. /etc/php/8.3/fpm/pool.d/www.conf):

INI
pm = dynamic
pm.max_children = 120
pm.start_servers = 12
pm.min_spare_servers = 6
pm.max_spare_servers = 18

Edit php.ini for both FPM and CLI (/etc/php/8.3/fpm/php.ini and /etc/php/8.3/cli/php.ini):

INI
memory_limit = 512M
upload_max_filesize = 4G
post_max_size = 4G
max_execution_time = 3600
max_input_time = 3600
date.timezone = UTC
opcache.enable=1
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.memory_consumption=192
opcache.save_comments=1
opcache.revalidate_freq=60

Enable APCu for the PHP CLI so Nextcloud's command-line tool (occ) and cron jobs can utilize local caching without throwing errors:

Bash
echo "apc.enable_cli=1" | sudo tee -a /etc/php/8.3/cli/conf.d/20-apcu.ini

Restart PHP-FPM:

Bash
sudo systemctl restart php8.3-fpm

Step 5: Install Nginx

Bash
sudo apt -y install nginx

Create the web root and download Nextcloud. Check the official Nextcloud downloads page or the GitHub releases for the current stable tarball before running this — as of this writing, 34.0.2 is current:

Bash
cd /tmp
wget https://download.nextcloud.com/server/releases/latest.tar.bz2
wget https://download.nextcloud.com/server/releases/latest.tar.bz2.sha256
sha256sum -c latest.tar.bz2.sha256
sudo mkdir -p /var/www/nextcloud
sudo tar -xjf latest.tar.bz2 -C /var/www/nextcloud --strip-components=1
sudo chown -R www-data:www-data /var/www/nextcloud

The latest.tar.bz2 URL always resolves to the newest stable release, so this pulls whichever 34.x point release is current when you run it — always verify the checksum before extracting.

Step 6: Configure Nginx

Create /etc/nginx/sites-available/nextcloud.conf:

Nginx
upstream php-handler {
    server unix:/run/php/php8.3-fpm.sock;
}

server {
    listen 80;
    listen [::]:80;
    server_name cloud.yourdomain.com;

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

    client_max_body_size 4G;
    fastcgi_buffers 64 4K;

    add_header X-Content-Type-Options "nosniff";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag "noindex, nofollow";
    add_header X-Permitted-Cross-Domain-Policies "none";

    location = /robots.txt { allow all; log_not_found off; access_log off; }
    location = /.well-known/carddav { return 301 $scheme://$host/remote.php/dav; }
    location = /.well-known/caldav { return 301 $scheme://$host/remote.php/dav; }

    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }

    location ~ \.php(?:$|/) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        set $path_info $fastcgi_path_info;
        try_files $fastcgi_script_name =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $path_info;
        fastcgi_param HTTPS on;
        fastcgi_param modHeadersAvailable true;
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    location ~ \.(?:css|js|svg|gif|png|jpg|ico|wasm|tflite|map)$ {
        try_files $uri /index.php$request_uri;
        expires 6M;
        access_log off;
    }

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

Enable the site and test the config:

Bash
sudo ln -s /etc/nginx/sites-available/nextcloud.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Step 7: Get a Let's Encrypt SSL Certificate

Bash
sudo apt -y install certbot python3-certbot-nginx
sudo certbot --nginx -d cloud.yourdomain.com

Certbot will edit your Nginx config to redirect HTTP to HTTPS and set up auto-renewal via a systemd timer. Confirm renewal works with:

Bash
sudo certbot renew --dry-run

Step 8: Run the Nextcloud Installer

Visit https://cloud.yourdomain.com in a browser and complete the setup wizard, entering an admin username/password and the MariaDB credentials from Step 3 (host: localhost, database: nextcloud, user: ncadmin).

Alternatively, install headlessly from the command line, which is often cleaner on a server with no browser access:

Bash
sudo -u www-data php /var/www/nextcloud/occ maintenance:install \
  --database "mysql" \
  --database-name "nextcloud" \
  --database-user "ncadmin" \
  --database-pass "REPLACE_WITH_A_STRONG_PASSWORD" \
  --admin-user "admin" \
  --admin-pass "REPLACE_WITH_A_STRONG_ADMIN_PASSWORD" \
  --data-dir "/var/www/nextcloud/data"

Add your trusted domain to config/config.php if it isn't picked up automatically:

Bash
sudo -u www-data php /var/www/nextcloud/occ config:system:set trusted_domains 1 --value=cloud.yourdomain.com

Step 9: Install and Configure Redis

Redis dramatically improves performance by handling file locking and transactional caching outside the database.

Bash
sudo apt -y install redis-server php-redis
sudo systemctl enable --now redis-server

Edit /etc/redis/redis.conf to listen on a Unix socket for lower latency:

INI
unixsocket /var/run/redis/redis.sock
unixsocketperm 770

Add www-data to the redis group so PHP-FPM can access the socket:

Bash
sudo usermod -aG redis www-data
sudo systemctl restart redis-server

Then tell Nextcloud to use Redis for locking and caching by editing config/config.php:

PHP
'memcache.local' => '\OC\Memcache\APCu',
'memcache.locking' => '\OC\Memcache\Redis',
'memcache.distributed' => '\OC\Memcache\Redis',
'redis' => [
     'host' => '/var/run/redis/redis.sock',
     'port' => 0,
     'timeout' => 0.0,
],

Restart PHP-FPM to apply:

Bash
sudo systemctl restart php8.3-fpm

Step 10: Set Up the Background Cron Job

Nextcloud needs a recurring job for maintenance tasks like file scans, notifications, and cleanup. The Cron method is more efficient than AJAX for anything beyond a hobby install.

Bash
sudo crontab -u www-data -e

Add:

Code Snippet
*/5 * * * * php -f /var/www/nextcloud/cron.php

Then set the background job type in the Nextcloud admin settings (Administration → Basic settings → Background jobs) to Cron, or via occ:

Bash
sudo -u www-data php /var/www/nextcloud/occ background:cron

Step 11: Run the Built-In Security and Setup Check

Bash
sudo -u www-data php /var/www/nextcloud/occ check

Then log in as admin and check Administration → Overview, which flags missing PHP modules, missing indexes, and configuration warnings directly in the UI. Common items it will ask you to fix at this stage:

  • Missing database indexes — run sudo -u www-data php /var/www/nextcloud/occ db:add-missing-indices
  • Missing PHP OPcache configuration — confirmed already set above
  • A recommended default_phone_region in config.php if you use the Talk or Contacts apps

Step 12: Harden and Finish

A few final steps that matter for a production dedicated server:

  • Enable HSTS in Nginx once you've confirmed HTTPS works reliably: add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
  • Set the correct data directory permissions and keep the data directory outside the web root if possible, for defense in depth.
  • Configure automatic updates for security patches at the OS level with unattended-upgrades, and keep Nextcloud itself updated through the built-in Updater app or occ upgrade after downloading new releases — Nextcloud publishes security fixes roughly every month, and its own documentation is explicit that delaying updates leaves known, disclosed vulnerabilities exposed.
  • Enable fail2ban with the Nextcloud filter to block brute-force login attempts against the web UI.
  • Set up regular backups of both the MariaDB database and the data directory — a mysqldump combined with an rsync or restic job to off-server storage is a reasonable baseline.

Keeping Nextcloud Updated

Nextcloud follows a yearly major-version cadence (roughly every four months for a new major line, each supported for about a year of monthly maintenance releases). Skipping major versions during an upgrade isn't supported, so always update to the latest point release of your current major version before jumping to the next one. The built-in Updater app (Administration → Overview → Updates) handles this safely for most single-server setups, or you can run:

Bash
sudo -u www-data php /var/www/nextcloud/updater/updater.phar

Troubleshooting Notes

  • "Untrusted domain" error on first load: add the domain to trusted_domains in config/config.php as shown in Step 8.
  • 413 Request Entity Too Large on large uploads: check both client_max_body_size in Nginx and upload_max_filesize/post_max_size in php.ini; all three need to agree.
  • Redis socket permission errors: confirm www-data is actually in the redis group (groups www-data) and that the socket path in config.php matches redis.conf exactly.
  • MariaDB version warning during setup: if you later upgrade MariaDB past 11.4 via a third-party repo, Nextcloud's setup checks may flag it as untested; this is a warning, not a hard failure, but sticking to Ubuntu's shipped 10.11 avoids the message entirely.

This setup gives you a self-hosted Nextcloud instance with proper caching, HTTPS, and background job handling — the same foundation used for production dedicated-server deployments rather than a quick home-lab install.