Drupal is an open source content management system suited to structured, multilingual and high-traffic sites. Modern Drupal is managed with Composer, which installs Drupal core, contributed modules and their PHP dependencies, and with Drush, its command line tool. In this tutorial you will set up Apache, PHP-FPM 8.3 and MariaDB on Ubuntu 24.04, create a Drupal 11 project with Composer, install the site with Drush, and finish with HTTPS, trusted host settings and cron.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS with at least 2 GB of RAM, for example a CubePath VPS. Composer needs the memory when resolving dependencies.
- A non-root user with
sudoprivileges, referred to asyour_user. - A domain name with an A record pointing to the server, referred to as
your_domain. - Ports 80 and 443 open to the internet.
Drupal 11 requires PHP 8.3 or newer and MariaDB 10.6 or newer. Ubuntu 24.04 ships PHP 8.3 and MariaDB 10.11, so the distribution packages are enough.
Step 1 - Installing Apache and PHP-FPM
Install Apache, PHP-FPM and the PHP extensions Drupal needs (gd for image styles, xml and mbstring for text handling, apcu and opcache for performance), plus unzip and git for Composer:
sudo apt update
sudo apt install apache2 php-fpm php-cli php-mysql php-gd php-xml php-mbstring php-curl php-zip php-apcu unzip git
Apache talks to PHP-FPM through mod_proxy_fcgi. Enable it together with mod_rewrite, which Drupal uses for clean URLs, and the PHP-FPM configuration snippet shipped by the package:
sudo a2enmod proxy_fcgi setenvif rewrite
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2
Check that PHP-FPM is running:
systemctl is-active php8.3-fpm apache2
active
active
Allow web traffic through the firewall:
sudo ufw allow OpenSSH
sudo ufw allow 'Apache Full'
sudo ufw enable
Step 2 - Tuning PHP settings
The defaults for memory and upload size are too low for a typical Drupal site. Instead of editing php.ini, add a small override file that survives package upgrades:
sudo nano /etc/php/8.3/fpm/conf.d/90-drupal.ini
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120
Restart PHP-FPM and confirm the new value:
sudo systemctl restart php8.3-fpm
sudo php-fpm8.3 -i | grep ^memory_limit
memory_limit => 256M => 256M
Step 3 - Creating the database
Install MariaDB:
sudo apt install mariadb-server
Open the MariaDB shell. The root account uses socket authentication on Ubuntu, so sudo is enough:
sudo mariadb
Create the database and a user limited to it. Replace your_strong_password with a long random password:
CREATE DATABASE drupal CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'drupal'@'localhost' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON drupal.* TO 'drupal'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Test the login:
mariadb -u drupal -p -e "SELECT CURRENT_USER();"
+------------------+
| CURRENT_USER() |
+------------------+
| drupal@localhost |
+------------------+
Step 4 - Creating the Drupal project with Composer
Install Composer from the Ubuntu archive:
sudo apt install composer
composer --version
Composer should never run as root or as the web server user. Create the project directory and give it to your own user:
sudo mkdir -p /var/www/drupal
sudo chown "$USER":"$USER" /var/www/drupal
Create the project from the official drupal/recommended-project template. It pins every dependency to the versions tested with the current Drupal release and places the public files in a web/ subdirectory:
composer create-project drupal/recommended-project /var/www/drupal
Add Drush to the project. It is installed per project, not globally:
cd /var/www/drupal
composer require drush/drush
Verify it:
vendor/bin/drush --version
Drush Commandline Tool 13.6.2.0
Step 5 - Installing the site with Drush
Drush can run the full installer from the command line, which is faster and more repeatable than the browser wizard. Replace the password, site name and admin email:
cd /var/www/drupal
vendor/bin/drush site:install standard \
--db-url='mysql://drupal:your_strong_password@localhost/drupal' \
--site-name='My Drupal Site' \
--account-name=admin \
--account-mail=you@your_domain
Confirm with yes. Drush creates web/sites/default/settings.php, the files directory and the database tables, and prints the generated admin password at the end:
[success] Installation complete. User name: admin User password: 8kVx2...
Store that password in your password manager. You can change it later from the user profile.
The web server needs to write only to the public files directory. Give its group to www-data and make it group writable, while the rest of the code stays owned by your user and read-only for Apache:
sudo chown -R "$USER":www-data /var/www/drupal/web/sites/default/files
sudo chmod -R g+w /var/www/drupal/web/sites/default/files
sudo find /var/www/drupal/web/sites/default/files -type d -exec chmod g+s {} +
The setgid bit on the directories makes files created later inherit the www-data group.
Step 6 - Configuring the Apache virtual host
Create a virtual host whose document root is the web/ directory, never the project root, so vendor/ and composer.json are not reachable from the internet:
sudo nano /etc/apache2/sites-available/drupal.conf
<VirtualHost *:80>
ServerName your_domain
ServerAlias www.your_domain
DocumentRoot /var/www/drupal/web
<Directory /var/www/drupal/web>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/drupal_error.log
CustomLog ${APACHE_LOG_DIR}/drupal_access.log combined
</VirtualHost>
AllowOverride All lets Apache apply the .htaccess file that ships with Drupal, which contains the clean URL rewrites and blocks access to sensitive files. Enable the site, disable the default one and test the configuration:
sudo a2ensite drupal.conf
sudo a2dissite 000-default.conf
sudo apache2ctl configtest
Syntax OK
Reload Apache:
sudo systemctl reload apache2
Step 7 - Enabling HTTPS with Let's Encrypt
Install Certbot with the Apache plugin and request a certificate. Certbot creates the HTTPS virtual host and a redirect from HTTP:
sudo apt install certbot python3-certbot-apache
sudo certbot --apache -d your_domain -d www.your_domain
Renewal runs from a systemd timer installed by the package. Confirm that it works:
sudo certbot renew --dry-run
Open https://your_domain/user/login and sign in with the admin account from Step 5.
Step 8 - Securing settings.php
Drupal rejects requests for unknown host names only if you tell it which ones are valid. The installer may have already made the settings file read-only, so give your user write access first:
chmod u+w /var/www/drupal/web/sites/default /var/www/drupal/web/sites/default/settings.php
Open the settings file:
nano /var/www/drupal/web/sites/default/settings.php
Add the following at the end of the file, escaping the dots in your domain:
$settings['trusted_host_patterns'] = [
'^your_domain$',
'^www\.your_domain$',
];
For example, for example.com the first pattern is '^example\.com$'. Then make the settings file and its directory read-only:
chmod 444 /var/www/drupal/web/sites/default/settings.php
chmod 555 /var/www/drupal/web/sites/default
Rebuild the cache and check the status report from the command line:
cd /var/www/drupal
vendor/bin/drush cache:rebuild
vendor/bin/drush core:requirements --severity=2
A clean installation returns no errors. In the browser, Reports > Status report shows the same checks, including the trusted host setting.
Step 9 - Running cron from the system
Drupal needs cron to index content, clean up temporary files and check for updates. The standard profile runs it on page views ("automated cron"), which adds latency to visitor requests. Disable that module and schedule Drush instead:
cd /var/www/drupal
vendor/bin/drush pm:uninstall automated_cron
Open your user's crontab:
crontab -e
Add this line to run cron every 15 minutes:
*/15 * * * * cd /var/www/drupal && vendor/bin/drush cron --quiet
After 15 minutes, Reports > Status report shows the time of the last cron run.
Installing modules and updating Drupal
Contributed modules are added with Composer and enabled with Drush. For example, to add Admin Toolbar and Pathauto:
cd /var/www/drupal
composer require drupal/admin_toolbar drupal/pathauto
vendor/bin/drush pm:install admin_toolbar pathauto
To update Drupal core and its dependencies, back up the database first, then run the database updates and rebuild the cache:
cd /var/www/drupal
vendor/bin/drush sql:dump --gzip --result-file="$HOME/drupal-before-update.sql"
composer update "drupal/core-*" --with-all-dependencies
vendor/bin/drush updatedb
vendor/bin/drush cache:rebuild
Troubleshooting
"The provided host name is not valid for this server." The domain you are using is not in trusted_host_patterns. Check the regular expressions in settings.php, including the escaped dots.
The files directory is not writable. The status report complains when www-data cannot write to web/sites/default/files. Repeat the ownership commands from Step 5.
Composer runs out of memory. Run it with COMPOSER_MEMORY_LIMIT=-1 composer ... or add swap to servers with 1 GB of RAM.
Conclusion
Drupal 11 is now running on Ubuntu 24.04 with Apache, PHP-FPM 8.3 and MariaDB, installed with Composer, served over HTTPS and maintained with Drush and system cron. As next steps, configure a regular database and files backup, add Redis for the cache backend on busy sites, and subscribe to the Drupal security advisories to apply core and module updates promptly.
