A manual Nextcloud LEMP deployment puts the application behind Nginx and PHP-FPM while MariaDB stores the application data on the same Ubuntu host. This layout fits operators who want package-managed services and the standard archive install instead of Nextcloud AIO, Snap, or a container stack.
The deployment depends on Nginx for HTTP requests, PHP-FPM for PHP execution, MariaDB for the database, the extracted Nextcloud archive, and the occ command that writes the initial configuration. Use the PHP-FPM package, service, and socket that match the active Ubuntu and Nextcloud release.
Assume a clean server with DNS already pointing at the chosen hostname. The bootstrap server block is HTTP-only so the installed site can be verified through Nginx before normal users log in; enable HTTPS and then review background jobs, cache, memory, and OPcache before putting the server into routine use.
$ sudo apt update
$ sudo apt install nginx mariadb-server php8.5-fpm php8.5-cli php8.5-gd php8.5-mysql php8.5-curl php8.5-mbstring php8.5-intl php8.5-gmp php8.5-xml php-imagick php8.5-zip php8.5-apcu bzip2 curl ca-certificates ##### snipped ##### Setting up nginx Setting up mariadb-server Setting up php8.5-fpm
php-imagick follows the package name used by the Ubuntu archive. If a PHP module name is already bundled or renamed in the active Ubuntu release, confirm it with the distribution package list before adding a third-party PHP repository.
$ sudo systemctl enable --now nginx mariadb php8.5-fpm
$ sudo mysql Welcome to the MariaDB monitor. ##### snipped ##### MariaDB [(none)]>
CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'replace-with-a-long-random-password'; GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost'; FLUSH PRIVILEGES; EXIT;
Use a unique database password and keep it out of screenshots, tickets, and shared transcripts.
$ curl -LO https://download.nextcloud.com/server/releases/latest.tar.bz2
$ curl -LO https://download.nextcloud.com/server/releases/latest.tar.bz2.sha256
$ sha256sum -c latest.tar.bz2.sha256 latest.tar.bz2: OK
Signature verification with the Nextcloud release key gives stronger provenance when deployment policy requires it.
$ sudo tar -xjf latest.tar.bz2 -C /var/www
Move or back up an existing /var/www/nextcloud directory before extracting. Extracting over an existing tree can mix releases and leave stale application files behind.
$ sudo chown -R www-data:www-data /var/www/nextcloud
$ sudo -E -u www-data php /var/www/nextcloud/occ maintenance:install \ --database 'mysql' \ --database-name 'nextcloud' \ --database-user 'nextcloud' \ --database-pass 'replace-with-database-password' \ --admin-user 'admin' \ --admin-pass 'replace-with-admin-password' Nextcloud was successfully installed
The database and admin passwords appear in the shell command while this runs. Use a controlled setup shell, clear shell history if required by local policy, and do not reuse the database password for the web admin account.
$ sudo -E -u www-data php /var/www/nextcloud/occ config:system:set trusted_domains 1 --value=cloud.example.com System config value trusted_domains => 1 set to string cloud.example.com
$ sudo -E -u www-data php /var/www/nextcloud/occ config:system:set overwrite.cli.url --value=http://cloud.example.com System config value overwrite.cli.url set to string http://cloud.example.com
Change this value to https://cloud.example.com after TLS is enabled.
Related: How to configure HTTPS for Nextcloud with Nginx and Let's Encrypt
$ sudoedit /etc/nginx/sites-available/cloud.example.com
upstream php-handler {
server unix:/run/php/php8.5-fpm.sock;
}
server {
listen 80;
listen [::]:80;
server_name cloud.example.com;
root /var/www/nextcloud;
index index.php index.html /index.php$request_uri;
client_max_body_size 512M;
location ^~ /.well-known {
location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; }
return 301 /index.php$request_uri;
}
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) {
return 404;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
return 404;
}
location ~ \.php(?:$|/) {
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_param HTTPS off;
fastcgi_pass php-handler;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
}
The official Nextcloud Nginx example changes over time. Keep the deny locations, .well-known redirects, PHP FastCGI parameters, asset caching rules, and PHP-FPM socket aligned with the active Nextcloud and Ubuntu release before production use.
This HTTP block is for initial bootstrap or private validation only. Do not let real users enter credentials over plain HTTP.
$ sudo ln -s /etc/nginx/sites-available/cloud.example.com /etc/nginx/sites-enabled/cloud.example.com
$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Related: How to test Nginx configuration
$ sudo systemctl reload nginx php8.5-fpm
Use the PHP-FPM unit that owns the socket in the site file.
Related: How to manage the Nginx service
Related: How to manage a Linux service with systemctl
$ curl http://cloud.example.com/status.php
{"installed":true,"maintenance":false,"needsDbUpgrade":false,"version":"35.0.0.0","versionstring":"35.0.0","edition":"","productname":"Nextcloud","extendedSupport":false}
A successful response proves DNS, Nginx, PHP-FPM, the web root, and the installed Nextcloud app can complete a request path.
$ sudo -E -u www-data php /var/www/nextcloud/occ status - installed: true - version: 35.0.0.0 - versionstring: 35.0.0 - edition: - maintenance: false - needsDbUpgrade: false - productname: Nextcloud - extendedSupport: false
Complete the follow-up service hardening before normal use, especially HTTPS, background jobs, memory limits, OPcache, and cache locking.
Related: How to configure HTTPS for Nextcloud with Nginx and Let's Encrypt
Related: How to configure Nextcloud background jobs with cron
Related: How to set the PHP memory limit for Nextcloud
Related: How to configure PHP OPcache for Nextcloud
Related: How to configure Redis caching for Nextcloud