Install Nextcloud with Nginx on Ubuntu 24.04

Install Nextcloud with Nginx on Ubuntu 24.04

Bitnesia Infrastructure Apr 10, 2026 221 ID

Cloud storage services like Google Drive and Dropbox are indeed practical, but they share the same limitation: your data is stored on third-party servers. Surrendering control of your data to other parties poses risks that are not always acceptable for privacy-conscious individuals or organizations bound by data regulations such as GDPR or national data protection laws.

Nextcloud is a self-hosted, open-source cloud storage platform that enables you to build your own cloud storage service on a VPS or private server. With Nextcloud, you gain full control over your data regarding who accesses it, where it is stored, and how it is managed. Beyond file sync features, Nextcloud also supports calendars, contacts, document collaboration, and hundreds of additional applications through its official App Store.

Why Nginx, Not Apache?

Apache HTTP Server is a proven web server, but its thread-per-connection architecture is less efficient when handling many simultaneous connections. Nginx uses an event-driven, non-blocking I/O model capable of handling thousands of concurrent connections with significantly lower memory usage. Nginx is a more suitable choice in terms of performance and resource efficiency for Nextcloud, which often serves many clients simultaneously such as desktop sync clients, mobile apps, and WebDAV.

Prerequisites

  • Server with Ubuntu 24.04 LTS OS (fresh install recommended).
  • Root access or a user with sudo privileges.
  • A domain or subdomain already pointed to your server IP, e.g., nextcloud.example.com. DNS propagation must be complete before requesting an SSL certificate.
  • Minimum specification of 1 vCPU and 1 GB RAM, though 2 GB RAM is recommended for a lightweight installation.

Environment Used

ComponentVersion / Details
OSUbuntu 24.04 LTS
Web ServerNginx (latest stable)
PHPPHP 8.3-FPM
DatabaseMariaDB 10.11
SSLLet's Encrypt (Certbot)
NextcloudLatest version
CacheRedis

1. System Preparation

Ensure the package list and operating system are fully updated before installing any packages. This step prevents dependency conflicts and ensures you receive the latest security patches.

Update and Upgrade

sudo apt update
sudo apt upgrade -y

Install Supporting Utilities

Install several basic utilities needed during the installation process. These include curl for downloading files, unzip for extracting the Nextcloud package, and gnupg for verifying packages from external repositories.

sudo apt install -y curl unzip gnupg2 ca-certificates lsb-release

Firewall Configuration (UFW)

Open required ports if UFW (Uncomplicated Firewall) is active on your server. These include port 22 for SSH, 80 for HTTP (required by Certbot during domain validation), and 443 for HTTPS.

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

LEMP Stack Installation

The LEMP stack is the software combination that forms the foundation of Nextcloud: Linux as the OS, Nginx as the web server, MariaDB as the database, and PHP as the runtime programming language. The last three components are installed separately for more controlled configuration.

Install Nginx

Install Nginx from Ubuntu's official repository, then enable its service to start automatically when the server boots.

sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

Verify Nginx is running:

sudo systemctl status nginx

The output should show status active (running). You can also open your server IP in a browser to confirm the default Nginx page appears.

● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Sat 2026-04-11 04:16:18 WIB; 34min ago
       Docs: man:nginx(8)
   Main PID: 1737 (nginx)
      Tasks: 3 (limit: 2315)
     Memory: 2.4M (peak: 5.2M)
        CPU: 28ms
     CGroup: /system.slice/nginx.service
             ├─1737 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ├─1740 "nginx: worker process"
             └─1741 "nginx: worker process"

Apr 11 04:16:18 ubuntu-server systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...

Install MariaDB

MariaDB is a MySQL fork actively maintained by the open-source community. Ubuntu 24.04 includes MariaDB 10.11 in its official repository.

sudo apt install -y mariadb-server
sudo systemctl enable mariadb
sudo systemctl start mariadb

Secure MariaDB Installation

Run the mysql_secure_installation script to secure your MariaDB installation, such as setting the root password, removing anonymous users, disabling remote root login, and deleting the test database.

sudo mysql_secure_installation

Answer each prompt as follows:

Enter current password for root (enter for none): [press Enter]
Switch to unix_socket authentication [Y/n]: n
Change the root password? [Y/n]: Y
New password: [enter a strong password]
Re-enter new password: [repeat password]
Remove anonymous users? [Y/n]: Y
Disallow root login remotely? [Y/n]: Y
Remove test database and access to it? [Y/n]: Y
Reload privilege tables now? [Y/n]: Y

Install PHP 8.3-FPM and Extensions

Nextcloud requires several PHP extensions for all its features to function properly. Install PHP 8.3 along with all required extensions in a single command.

sudo apt install -y \
  php8.3 \
  php8.3-fpm \
  php8.3-common \
  php8.3-cli \
  php8.3-gd \
  php8.3-mysql \
  php8.3-curl \
  php8.3-xml \
  php8.3-zip \
  php8.3-mbstring \
  php8.3-bz2 \
  php8.3-intl \
  php8.3-bcmath \
  php8.3-gmp \
  php8.3-imagick \
  php8.3-redis \
  php8.3-apcu

Verify the installed PHP version:

php -v

Example output:

PHP 8.3.6 (cli) (built: Mar 20 2026 02:32:55) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.6, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.6, Copyright (c), by Zend Technologies

Configure php.ini for Nextcloud

Open the PHP-FPM configuration file and adjust several important values to ensure Nextcloud runs optimally, especially for large file uploads.

sudo nano /etc/php/8.3/fpm/php.ini

Find and modify the following values (use Ctrl+W to search in nano):

memory_limit = 512M
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 360
date.timezone = Asia/Jakarta

Enable OPcache, which is highly recommended by Nextcloud:

opcache.enable = 1
opcache.memory_consumption = 64
opcache.interned_strings_buffer = 64
opcache.max_accelerated_files = 10000
opcache.save_comments = 1
opcache.revalidate_freq = 1

Reload PHP-FPM after configuration changes:

sudo systemctl restart php8.3-fpm
sudo systemctl enable php8.3-fpm

Verify Nginx is running:

sudo systemctl status php8.3-fpm

Example output:

● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
     Loaded: loaded (/usr/lib/systemd/system/php8.3-fpm.service; enabled; preset: enabled)
     Active: active (running) since Sat 2026-04-11 04:47:38 WIB; 5min ago
       Docs: man:php-fpm8.3(8)
    Process: 16548 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/8.3/fpm/pool.d/www.conf 83 (code=exited, status=0/SUCCESS)
   Main PID: 16545 (php-fpm8.3)
     Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec"
      Tasks: 3 (limit: 2315)
     Memory: 26.2M (peak: 26.9M)
        CPU: 93ms
     CGroup: /system.slice/php8.3-fpm.service
             ├─16545 "php-fpm: master process (/etc/php/8.3/fpm/php-fpm.conf)"
             ├─16546 "php-fpm: pool www"
             └─16547 "php-fpm: pool www"

Apr 11 04:47:38 ubuntu-server systemd[1]: Starting php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager.

2. Database Configuration

Nextcloud requires its own database with a user that has full privileges on that database. This principle of least privilege is important for security so that if the Nextcloud database account is compromised, the impact is limited only to the Nextcloud database.

Log in to MariaDB as root:

sudo mariadb -u root

Run the following commands sequentially within the MariaDB shell:

Create Database

CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

Create Database User

Replace your_strong_password with a complex password. Note this password as it will be needed during Nextcloud setup via browser.

CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'your_strong_password';

Grant Privileges

GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Verify the database was created successfully:

sudo mariadb -u root -e "SHOW DATABASES;"

Ensure the nextcloud database appears in the list.

3. Download and Configure Nextcloud

Download Nextcloud Package

Download the latest Nextcloud package directly from the official Nextcloud server. Using the latest.zip URL ensures you always get the latest stable version without manually checking version numbers.

wget https://download.nextcloud.com/server/releases/latest.zip -O /tmp/nextcloud.zip

Extract to Web Root Directory

Extract the package to the /var/www/ directory. Nginx will be configured to use /var/www/nextcloud as the document root.

sudo unzip /tmp/nextcloud.zip -d /var/www/
sudo mkdir -p /var/www/nextcloud/data

The data directory is where all Nextcloud user files will be stored. Placement within the document root is safe because Nginx is configured to block direct access to this directory.

Set Permissions

Change ownership of the entire Nextcloud directory to the www-data user, the system user used by Nginx and PHP-FPM to access files.

sudo chown -R www-data:www-data /var/www/nextcloud
sudo chmod -R 750 /var/www/nextcloud

4. Configure Nginx Server Block

Nginx configuration for Nextcloud is more complex than standard web server configuration because it must handle WebDAV, pretty URLs, protection of sensitive directories, security headers, and optimization for large file uploads.

Create Server Block Configuration File

Create a new configuration file. Replace nextcloud.example.com with your actual domain or subdomain name.

sudo nano /etc/nginx/sites-available/nextcloud.conf

Initial Configuration (Before SSL)

Start with a simple HTTP configuration. This configuration is required first so Certbot can perform domain validation via HTTP before issuing an SSL certificate.

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

map $arg_v $asset_immutable {
    ""      "";
    default ", immutable";
}

server {
    listen 80;
    listen [::]:80;
    server_name nextcloud.example.com;

    server_tokens off;

    root /var/www/nextcloud;

    location /.well-known/acme-challenge {
        root /var/www/nextcloud;
        try_files $uri $uri/ =404;
    }
}

Enable the configuration and test Nginx syntax:

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

Proceed to the SSL installation step if nginx -t displays syntax is ok and test is successful.

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

5. Install SSL with Let's Encrypt

Let's Encrypt is a nonprofit Certificate Authority (CA) that issues SSL/TLS certificates for free. Certbot is the official tool for automating the request, verification, and renewal process for certificates. HTTPS access is not only about security but is also used by Google as one of the SEO ranking factors.

Install Certbot

sudo apt install -y certbot python3-certbot-nginx

Request SSL Certificate

Replace nextcloud.example.com with your domain, and [email protected] with a valid email address for certificate expiration notifications.

sudo certbot --non-interactive \
  --agree-tos \
  --no-eff-email \
  -m [email protected] \
  --nginx \
  -d nextcloud.example.com

Certbot will validate domain ownership via the ACME protocol, download the certificate, and store it at /etc/letsencrypt/live/nextcloud.example.com/.

Verify Auto-Renewal

Certbot automatically adds a systemd timer to renew certificates before they expire. Verify the timer is active with the following command:

sudo systemctl status certbot.timer

Test the renewal process manually (dry-run) to ensure the system works without actually performing a renewal:

sudo certbot renew --dry-run

6. Final Nginx Configuration with SSL

Update the Nginx configuration with the full configuration after the SSL certificate has been successfully generated. This configuration includes HTTPS, security headers, gzip compression, and all routing rules required by Nextcloud.

Reopen the configuration file:

sudo nano /etc/nginx/sites-available/nextcloud.conf

Replace its entire content with the following configuration:

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

map $arg_v $asset_immutable {
    ""      "";
    default ", immutable";
}

# Redirect HTTP ke HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name nextcloud.example.com;

    server_tokens off;

    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name nextcloud.example.com;

    # Path ke direktori Nextcloud
    root /var/www/nextcloud;

    # Sertifikat SSL dari Let's Encrypt
    ssl_certificate     /etc/letsencrypt/live/nextcloud.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nextcloud.example.com/privkey.pem;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Sembunyikan versi Nginx
    server_tokens off;

    # Ukuran maksimal upload dan timeout
    client_max_body_size 100M;
    client_body_timeout 300s;
    fastcgi_buffers 64 4K;
    client_body_buffer_size 512k;

    # Aktifkan gzip compression
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types
        application/atom+xml text/javascript application/javascript
        application/json application/ld+json application/manifest+json
        application/rss+xml application/xhtml+xml application/xml
        font/opentype image/bmp image/svg+xml image/x-icon
        text/cache-manifest text/css text/plain text/vcard
        text/vnd.rim.location.xloc text/vtt text/x-component
        text/x-cross-domain-policy;

    # MIME types tambahan untuk Nextcloud
    include mime.types;
    types {
        text/javascript mjs;
    }

    # Security headers
    add_header Referrer-Policy                   "no-referrer"       always;
    add_header X-Content-Type-Options            "nosniff"           always;
    add_header X-Frame-Options                   "SAMEORIGIN"        always;
    add_header X-Permitted-Cross-Domain-Policies "none"              always;
    add_header X-Robots-Tag                      "noindex, nofollow" always;
    fastcgi_hide_header X-Powered-By;

    index index.php index.html /index.php$request_uri;

    # Robots.txt
    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # Tangani /.well-known untuk WebDAV dan ACME challenge
    location ^~ /.well-known {
        location = /.well-known/carddav { return 301 /remote.php/dav/; }
        location = /.well-known/caldav  { return 301 /remote.php/dav/; }

        location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
        location /.well-known/pki-validation    { try_files $uri $uri/ =404; }

        return 301 /index.php$request_uri;
    }

    # Tangani Microsoft DAV clients
    location = / {
        if ( $http_user_agent ~ ^DavClnt ) {
            return 302 /remote.php/webdav/$is_args$args;
        }
    }

    # Blokir akses ke direktori dan file sensitif
    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }

    # Pass request PHP ke PHP-FPM
    location ~ \.php(?:$|/) {
        rewrite ^/(?!index|remote|public|cron|status|ocs\/v[12]|ocs-provider\/.+|core\/ajax\/update|updater\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri;

        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        set $path_info $fastcgi_path_info;

        try_files $fastcgi_script_name =404;

        include fastcgi_params;
        fastcgi_pass php-handler;

        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_intercept_errors on;
        fastcgi_request_buffering on;
        fastcgi_max_temp_file_size 0;
    }

    # Static assets — cache agresif dengan immutable flag
    location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac|mp4|webm)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463$asset_immutable";
        add_header Referrer-Policy                   "no-referrer"       always;
        add_header X-Content-Type-Options            "nosniff"           always;
        add_header X-Frame-Options                   "SAMEORIGIN"        always;
        add_header X-Permitted-Cross-Domain-Policies "none"              always;
        add_header X-Robots-Tag                      "noindex, nofollow" always;
        access_log off;
    }

    # Font files
    location ~ \.(otf|woff2?)$ {
        try_files $uri /index.php$request_uri;
        expires 7d;
        access_log off;
    }

    # Redirect /remote ke /remote.php
    location /remote {
        return 301 /remote.php$request_uri;
    }

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

    access_log /var/log/nginx/nextcloud.example.com_access.log;
    error_log /var/log/nginx/nextcloud.example.com_error.log;    
}

Test Nginx syntax and reload:

sudo nginx -t
sudo systemctl reload nginx

Ensure there are no errors before proceeding. The most common errors are usually typos or incorrect SSL certificate paths.

7. Setup Nextcloud via Web Browser

The final installation step is performed through a browser using Nextcloud's built-in Installation Wizard after all components are installed and configured.

Access Domain in Browser

Open a browser and navigate to your domain: https://nextcloud.example.com. The Nextcloud installation page requesting some initial information will greet you if all configurations are correct.

Fill Out the Installation Form

Complete the form with the following information:

FieldValue
Username (Admin)Choose your Nextcloud admin username
Password (Admin)Strong password for the admin account
Data folder/var/www/nextcloud/data
Database hostlocalhost
Database namenextcloud
Database usernextcloud
Database passwordThe password you created

Click the Install button. The installation process usually takes 1–3 minutes depending on server specifications. Nextcloud will create database tables, install default applications, and initialize configuration.

Install Recommended Apps (Optional)

Nextcloud will offer the option to install recommended apps such as Calendar, Contacts, Mail, and Talk after installation is complete. You can click Install recommended apps or Skip to install them later through the Apps menu in the dashboard.

You will now be redirected to the Nextcloud Dashboard. The basic installation has been successfully completed.

8. Post-Installation Configuration (Recommended)

The basic installation is complete, but there are several additional configurations highly recommended to ensure Nextcloud runs with optimal performance, is reliable for long-term use, and is free from warnings on the Administration settings → Overview page.

Enable Memory Caching with Redis

By default, Nextcloud does not use memory caching, causing many unnecessary repeated database operations. Redis is an in-memory data store that can be used as a local cache and distributed lock by Nextcloud to significantly improve performance.

Install Redis and its PHP extension:

sudo apt install -y redis-server php8.3-redis
sudo systemctl enable redis-server
sudo systemctl start redis-server

Edit the Nextcloud configuration file config.php:

sudo nano /var/www/nextcloud/config/config.php

Add the following configuration inside the $CONFIG array, before the closing ); line:

  'memcache.local' => '\OC\Memcache\Redis',
  'memcache.locking' => '\OC\Memcache\Redis',
  'redis' => [
    'host' => '127.0.0.1',
    'port' => 6379,
    'timeout' => 0.0,
  ],

Reload PHP-FPM for changes to take effect:

sudo systemctl restart php8.3-fpm

Configure Cron Job for Background Jobs

Nextcloud uses background jobs for various maintenance tasks such as cleaning temporary files, sending notifications, and updating metadata. Using AJAX as the default method is not ideal for production servers.

Change the setting to Cron as the background jobs method via the following command:

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

Then add a cron job for the www-data user:

sudo crontab -u www-data -e

Add the following line at the end of the file so background jobs run every 5 minutes:

*/5 * * * * /usr/bin/php -f /var/www/nextcloud/cron.php --no-interaction >> /tmp/nc-cron.log 2>&1

Configure Trusted Domains (If Needed)

Add your domain to the trusted domains list in config.php if Nextcloud displays an Access through untrusted domain error:

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

Keep Nextcloud Up-to-Date

Nextcloud releases regular updates for bug fixes and security patches. Run the following command via command line:

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

9. Common Troubleshooting

502 Bad Gateway

This error is usually caused by Nginx being unable to communicate with PHP-FPM, often due to an incorrect socket path in the Nginx configuration.

Check the active PHP-FPM socket path:

cat /etc/php/8.3/fpm/pool.d/www.conf | grep "^listen"

Ensure the value matches what is in upstream php-handler in the Nginx configuration. Also check if PHP-FPM is running with the following command:

sudo systemctl status php8.3-fpm

403 Forbidden

Check the ownership of the Nextcloud directory. Nginx and PHP-FPM must run as the www-data user:

ls -la /var/www/ | grep nextcloud
sudo chown -R www-data:www-data /var/www/nextcloud

File Upload Fails Above 10 MB

Ensure client_max_body_size in the Nginx configuration and upload_max_filesize and post_max_size in php.ini are set to sufficiently large values, e.g., 100M.

Check Logs for Diagnosis

Use the following commands to monitor logs in real-time:

# Nginx logs
sudo tail -f /var/log/nginx/nextcloud.example.com_error.log

# Nextcloud logs
sudo tail -f /var/www/nextcloud/data/nextcloud.log

# PHP-FPM logs
sudo tail -f /var/log/php8.3-fpm.log

Conclusion

Through the steps above, you have successfully built a secure, high-performance self-hosted cloud storage infrastructure using Nextcloud on Ubuntu 24.04 LTS. Using a LEMP stack combined with Redis as memory caching ensures the service remains responsive even when handling large amounts of data.

You now have full control over data privacy without dependence on third parties. Going forward, you can expand this server's functionality by installing various additional applications from the official Nextcloud App Store to support team productivity and collaboration.

Related Posts