This tutorial explains how to install Drupal 11 on an Ubuntu 24.04 LTS dedicated server using:
- Nginx
- PHP 8.3
- PHP-FPM
- MariaDB
- Composer
- Let's Encrypt SSL
- Drush
The installation uses Drupal's Composer-based project structure, with /web as the public document root.
Prerequisites
Before starting, prepare:
- A dedicated server running Ubuntu 24.04 LTS
- A domain name
- DNS access for the domain
- SSH access
- A non-root user with sudo privileges
- At least 1 GB of RAM
- Sufficient storage for Drupal, uploaded files, logs, and backups
- Domain: example.com
- Drupal directory: /var/www/drupal
- Database name: drupal
- Database user: drupaluser
1. Connect to the Dedicated Server
Connect to the server through SSH:
ssh username@SERVER_IP
Check the operating system:
lsb_release -a
Update the system and install basic utilities:
sudo apt update
sudo apt upgrade -y
sudo apt install curl unzip git ca-certificates dnsutils -y
If Ubuntu requires a reboot:
sudo reboot
Reconnect to the server after it becomes available.
2. Install Nginx
Install Nginx:
sudo apt install nginx -y
Enable and start the service, check its status, and verify the installed version:
sudo systemctl enable --now nginx
sudo systemctl status nginx
nginx -v
3. Install and Secure MariaDB
Install MariaDB:
sudo apt install mariadb-server -y
Enable and start MariaDB, then check the service:
sudo systemctl enable --now mariadb
sudo systemctl status mariadb
Run MariaDB's security script. Follow the prompts to remove unnecessary anonymous accounts and the test database, and to restrict unnecessary remote administrative access:
sudo mariadb-secure-installation
mariadb --version
4. Create the Drupal Database
Open the MariaDB shell:
sudo mariadb
Execute the following SQL queries to create the database, user, and assign privileges. Be sure to replace the placeholder password:
CREATE DATABASE drupal CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'drupaluser'@'localhost' IDENTIFIED BY 'CHANGE_THIS_TO_A_STRONG_PASSWORD';
GRANT ALL PRIVILEGES ON drupal.* TO 'drupaluser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Your database details for the final installation wizard are now:
- Database type: MySQL / MariaDB
- Database name: drupal
- Username: drupaluser
- Password: YOUR_DATABASE_PASSWORD
- Host: localhost
Use a strong, unique password for the database account.
5. Install PHP 8.3 and PHP Extensions
Drupal 11 supports PHP 8.3, and this guide uses PHP 8.3. Install PHP-FPM and the required extensions:
sudo apt install php8.3-fpm php8.3-cli php8.3-mysql php8.3-gd php8.3-curl php8.3-xml php8.3-mbstring php8.3-zip php8.3-intl php8.3-opcache -y
Check the PHP version, status, and enable PHP-FPM:
php -v
sudo systemctl status php8.3-fpm
sudo systemctl enable php8.3-fpm
Optional: APCu
APCu can be used for local application caching, but it is not a mandatory Drupal core requirement. If you want to install it:
sudo apt install php8.3-apcu -y
6. Install Composer
Check whether Composer is already available:
composer --version
If it is not installed:
sudo apt install composer -y
composer --version
Drupal 11 requires a sufficiently recent Composer 2 release, so verify that the installed version meets the requirements of the Drupal release you are deploying.
7. Create the Drupal Project
Create the application directory, give your deployment user ownership, and move into the directory:
sudo mkdir -p /var/www
sudo chown $USER:$USER /var/www
cd /var/www
Create the Drupal project using the official Composer project template:
composer create-project drupal/recommended-project drupal
/var/www/drupal/
├── composer.json
├── composer.lock
├── vendor/
└── web/
├── core/
├── modules/
├── profiles/
├── sites/
└── themes/
The public web root will be: /var/www/drupal/web. Do not point Nginx at /var/www/drupal. The /web directory should be the public document root.
8. Prepare Drupal Permissions for Installation
Drupal needs temporary write access to its settings file during installation. Move into the Drupal web directory, create settings.php from the default template, and set the project ownership:
cd /var/www/drupal/web
sudo cp sites/default/default.settings.php sites/default/settings.php
sudo chown -R $USER:www-data /var/www/drupal
Allow the web server group to write to settings.php during installation, allow the installer to work with the default site directory, create Drupal's public files directory, and configure permissions:
sudo chmod 660 sites/default/settings.php
sudo chmod 770 sites/default
sudo mkdir -p sites/default/files
sudo chown -R $USER:www-data sites/default/files
sudo find sites/default/files -type d -exec chmod 770 {} \;
sudo find sites/default/files -type f -exec chmod 660 {} \;
9. Configure Nginx
Remove Ubuntu's default enabled site:
sudo rm -f /etc/nginx/sites-enabled/default
Create the Drupal Nginx configuration: sudo nano /etc/nginx/sites-available/drupal
Use the following configuration and replace example.com with your actual domain:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/drupal/web;
index index.php index.html;
# Drupal front controller
location / {
try_files $uri /index.php?$query_string;
}
# Prevent PHP execution in Drupal's public files directory
location ~ ^/sites/[^/]+/files/.*\.php$ {
deny all;
}
# Block hidden files and directories
location ~ (^|/)\. {
deny all;
}
# Handle Drupal Image Styles
location ~ ^/sites/.*/files/styles/ {
try_files $uri /index.php?$query_string;
}
# Serve normal static assets directly
location ~* \.(?:css|js|jpg|jpeg|gif|png|ico|svg|webp)$ {
try_files $uri =404;
expires 30d;
access_log off;
log_not_found off;
}
# Pass PHP requests to PHP-FPM
location ~ '\.php$|^/update.php' {
fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
try_files $uri =404;
include fastcgi_params;
fastcgi_param HTTP_PROXY "";
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param QUERY_STRING $query_string;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
The important security and performance points here are:
- /var/www/drupal/web is the public root.
- PHP execution is blocked inside uploaded/public files.
- Hidden files are denied.
- Drupal Image Styles receive their own routing rule.
- Missing CSS, JavaScript, and normal image files return 404 without unnecessarily bootstrapping Drupal.
- PHP requests are checked against the requested URI before being passed to PHP-FPM.
- Nginx documents try_files $uri =404 as a way to verify that the requested PHP file exists before sending it to FastCGI.
Enable the site, test the configuration, and if successful, reload Nginx:
sudo ln -s /etc/nginx/sites-available/drupal /etc/nginx/sites-enabled/drupal
sudo nginx -t
sudo systemctl reload nginx
10. Point the Domain to the Server
Create DNS records pointing the domain to the dedicated server's public IP.
- For the root domain: Type: A, Name: @, Value: SERVER_IP
- For www: Type: A, Name: www, Value: SERVER_IP
Check DNS resolution:
dig example.com
Make sure the domain resolves to the correct server before requesting the SSL certificate.
11. Configure HTTPS with Let's Encrypt
Install Certbot, request the certificate, and follow Certbot's prompts:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com
Test the Nginx configuration, reload Nginx, and check the certificate renewal timer:
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status certbot.timer
sudo certbot renew --dry-run
Your Drupal installation should now be accessible through: https://example.com
12. Complete the Drupal Installation
Open https://example.com in your browser. The Drupal installation wizard should appear.
When prompted for database information, use:
- Database type: MySQL / MariaDB
- Database name: drupal
- Database username: drupaluser
- Database password: YOUR_DATABASE_PASSWORD
- Database host: localhost
Complete the remaining installation fields (Site name, Site email, Administrator username, Administrator password, Country, Time zone). Complete the installation and sign in to the Drupal administration interface.
13. Lock Down Drupal Permissions After Installation
Once Drupal has completed installation, remove unnecessary write access from settings.php, restrict the default site directory, and set the normal Drupal directory and file permissions:
sudo chmod a-w /var/www/drupal/web/sites/default/settings.php
sudo chmod 750 /var/www/drupal/web/sites/default
sudo find /var/www/drupal -type d -exec chmod 750 {} \;
sudo find /var/www/drupal -type f -exec chmod 640 {} \;
Restore the writable permissions required by Drupal's public files directory:
sudo find /var/www/drupal/web/sites/default/files -type d -exec chmod 770 {} \;
sudo find /var/www/drupal/web/sites/default/files -type f -exec chmod 660 {} \;
The intended model is that the deployment user owns the Drupal code while the www-data group can read the application and write only to directories that actually require web-server write access. Drupal's security guidance recommends preventing the web server from modifying the code it executes while allowing appropriate write access to the files directory.
14. Configure Trusted Host Patterns
Edit Drupal's settings file: sudo nano /var/www/drupal/web/sites/default/settings.php
Add:
$settings['trusted_host_patterns'] = [
'^example\.com$',
'^www\.example\.com$',
];
Replace the domains with your actual hostnames. Save the file and verify that Drupal can still load correctly.
15. Configure the Firewall
Allow SSH, HTTP, and HTTPS, then enable UFW and check the rules:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
Always make sure SSH is allowed before enabling the firewall.
16. Configure PHP
Check the active PHP configuration:
php --ini
Depending on the site's workload, you may configure values such as:
memory_limit = 256Mupload_max_filesize = 64Mpost_max_size = 64Mmax_execution_time = 120
These are example production values rather than universal Drupal requirements. Choose values appropriate for your modules, uploads, traffic, and available server resources.
After changing PHP-FPM configuration, restart it: sudo systemctl restart php8.3-fpm.
17. Check PHP OPcache
Check whether OPcache is loaded:
php -m | grep -i opcache
If you modify OPcache settings: sudo systemctl restart php8.3-fpm.
OPcache should be configured according to the application's workload and the server's available memory.
18. Install Drush and Configure Drupal Cron
Move to the Drupal project, install Drush through Composer, check the installation, and run Drupal cron manually first:
cd /var/www/drupal
composer require drush/drush
vendor/bin/drush status
vendor/bin/drush cron
If the command completes successfully, configure a system cron job. For this deployment, run the Drupal cron process as the same user that runs PHP-FPM: www-data.
Edit the www-data user's crontab:
sudo crontab -u www-data -e
Add this line. This runs Drupal cron every 15 minutes:
*/15 * * * * /usr/bin/php /var/www/drupal/vendor/bin/drush cron >/dev/null 2>&1
Running the job under the web-server user keeps files created by Drupal cron within the same ownership context as files created by PHP-FPM. Drupal's own cron documentation shows that a dedicated web/application user can be used for scheduled Drush execution, while Drupal's permissions guidance emphasizes keeping writable site data accessible to the web-server process.
19. Check Drupal's Status Report
Log in to Drupal and open: Administration → Reports → Status report.
Review the report for:
- PHP requirements
- Database configuration
- File permissions
- Trusted host configuration
- Security warnings
- Update information
- Configuration problems
Resolve critical issues before considering the deployment production-ready.
20. Configure Backups
A production Drupal installation should have both database and file backups.
Create a database backup:
mariadb-dump -u drupaluser -p drupal > drupal-backup.sql
Important Drupal site data includes: /var/www/drupal/web/sites/
The Composer files and dependency definitions should also be retained so the application can be reconstructed.
Do not store the only copy of your backups on the same dedicated server.
A proper backup strategy should account for:
- Server failure
- Disk failure
- Accidental deletion
- Database corruption
- Security incidents
Test restoration periodically rather than assuming that a successful backup command guarantees recoverability.
21. Monitor the Dedicated Server
Monitor the underlying server as well as Drupal. Important metrics include:
- CPU utilization
- Memory usage
- Disk capacity
- Disk I/O
- PHP-FPM activity
- Nginx activity
- MariaDB performance
- Network traffic
- System logs
Useful commands include:
top
free -h
df -h
Check the status of your essential services:
systemctl status nginx
systemctl status php8.3-fpm
systemctl status mariadb
22. Keep Drupal Updated
Use Composer to manage Drupal core and its dependencies.
Before applying updates:
- Back up the database.
- Back up important site files.
- Review dependency changes.
- Check contributed module and theme compatibility.
- Apply updates.
- Run required database updates.
- Clear caches when necessary.
- Check the Drupal Status report.
For Drupal core updates:
cd /var/www/drupal
composer update "drupal/core-*" --with-all-dependencies
Always review the resulting dependency changes before applying them to a production environment.
23. Final Production Checklist
Before going live, verify:
Conclusion
Installing Drupal 11 on a dedicated server requires more than installing a web server, PHP, and a database. A production deployment should also address database hardening, filesystem permissions, HTTPS, firewall configuration, trusted hosts, scheduled maintenance, backups, and server monitoring.
This setup uses Drupal's Composer-based project structure with /web as the public document root, keeping Composer dependencies outside the web-accessible directory.
The Nginx configuration also separates Drupal Image Styles from normal static assets, prevents PHP execution in public file directories, blocks hidden files, and verifies requested PHP files before passing them to PHP-FPM.
Finally, running scheduled Drupal cron tasks as the web-server user keeps Drupal-generated writable files within the same ownership context as PHP-FPM.
With these controls in place, the dedicated server provides a cleaner and more secure foundation for running a production Drupal website.
Need a Reliable Server for Drupal 11?
A fast CMS requires bare-metal performance that won't bottleneck under heavy visitor traffic or database queries.
Looking for the perfect hardware to run your web applications? Explore fitservers.com for high-performance, cost-effective dedicated servers. Whether you are running a single corporate site or multiple heavy web platforms, Fit Servers gives you the raw power and dedicated bandwidth required to keep your sites fast, secure, and always online.
Configure your dedicated server today!