How to Install Drupal 10 with Nginx, PHP-FPM 8.1, MySQL, phpMyAdmin on Ubuntu 23.04 - Linode Guide

  • The setup requires Ubuntu 23.04, root privileges, and basic security measures such as creating a limited user account, disabling root logins over SSH, and updating the system.
  • Nginx and PHP-FPM are the main components of the setup, with PHP-FPM being configured to serve HTTP requests for PHP pages using Nginx. A PHP info file can be used to test that php-fpm is working properly with Nginx.
  • To configure the VirtualHost for Drupal, the Nginx configuration for Drupal 10 needs to be pasted in the appropriate file. The configuration includes server name, root path, access and error logs, listen directives, and location directives to block access to certain files and directories.

| Servers | 543 seen

In this article, you will learn how to set up a webserver for serving Drupal websites running with Nginx, PHP-FPM 8.1, MySQL, and phpMyAdmin on Ubuntu 23.04

For the following tutorial, I used very much the information from my previous guide with 22.04, but I decided to rewrite it for Ubuntu 23.04 version as it is shipped with php8.1 version and will work with Drupal 10 (most probably with previous versions too)

If you need any assistance configuring servers or working with Drupal, consider hiring me at the Terramatris website


  • Ubuntu 23.04 
  • Root privileges.

You can get a cheap VPS starting at just $5/mo from Linode. That's what I did - bought a new nanode from Linode

Literally, in a couple of seconds, the new server was up and running - that's what I love about sticking with Linode for years

Follow the basic security guide, see: Securing Your Server

I will use Terminal from MAC to access  over SSH (Windows users could use Putty)

Secure your server

Create the user, replacing example_user with your desired username. You’ll then be asked to assign the user a password:

adduser example_user

Add the user to the sudo group so you’ll have administrative privileges:

adduser example_user sudo

Disallow root logins over SSH. This requires all SSH connections to be by non-root users. Once a limited user account is connected, administrative privileges are accessible either by using sudo or changing to a root shell using su -.

sudo nano /etc/ssh/sshd_config

Under # Authetification section change to

# Authentication: ...

PermitRootLogin no

Update the Ubuntu system

sudo apt-get update

Install Nginx and PHP-FPM

Install Nginx with the following apt command:

sudo apt-get install nginx -y

Next, install php8.1-fpm with php-gd extension that is required by Drupal core:

sudo apt-get install php8.1-fpm php8.1-cli php8.1-gd php8.1-mysql php8.1-xml -y

Configure Nginx and PHP-FPM

In this step, we will configure Nginx to use php-fpm to serve HTTP requests for PHP pages. Go to the php-fpm directory "/etc/php/8.1/fpm" and edit the "php.ini" file:

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

Un-comment the cgi.fix_pathinfo line and change the value to "0"

When using nano command you can use CTRL+W to locate that line. Once changed press CTRL+O to save changes and CTRL+X to exit from nano editor

Now we should modify the default Nginx virtual host configuration. Edit the "default" file and enable the php-fpm directive.

sudo nano /etc/nginx/sites-available/default

Un-comment  location ~ \.php$ section, so it look like this

location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php7.4-cgi alone:
# With php7.4-fpm:
fastcgi_pass unix:/run/php/php7.4-fpm.sock;


Then test the Nginx configuration with the command "nginx -t" to ensure that it is valid:

nginx -t

If there is no error, restart nginx and the php-fpm service:

systemctl restart nginx
systemctl restart php8.1-fpm

PHP Info file (Optional)

Next, test that php-fpm is working properly with Nginx by creating new PHP info file in the web directory "/var/www/html"

cd /var/www/html/
echo "<?php phpinfo(); ?>" > info.php

Visit the info.php file at the server IP  in a web browser.

Configure the VirtualHost for Drupal

We will install Drupal 10 in the directory "/srv/www/". Please replace my domain name in your installation with the domain name of the website that you want to use this Drupal installation for. So let's create the directory:

sudo mkdir -p /srv/www/{public_html,logs}
sudo usermod -a -G www-data admin
sudo chown -R www-data:www-data /srv/www
sudo chmod -R 775 /srv/www
sudo nano /etc/nginx/sites-available/

Paste the Nginx configuration for Drupal 10:

server {
    root /srv/www/; ## <-- Your only path $
    access_log /srv/www/;
    error_log /srv/www/;

    listen 80;
    listen [::]:80;

    location = /favicon.ico {
        log_not_found off;
        access_log off;

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

    # Very rarely should these ever be accessed outside of your lan
    location ~* \.(txt|log)$ {
        deny all;

    location ~ \..*/.*\.php$ {
        return 403;

    location ~ ^/sites/.*/private/ {
        return 403;

    # Block access to "hidden" files and directories whose names begin with a
    # period. This includes directories used by version control systems such
    # as Subversion or Git to store control files.
    location ~ (^|/)\. {
        return 403;

    location / {
        # try_files $uri @rewrite; # For Drupal <= 6
        try_files $uri /index.php?$query_string; # For Drupal >= 7

    location @rewrite {
        rewrite ^/(.*)$ /index.php?q=$1;

    # In Drupal 8, we must also match new paths where the '.php' appears in the middle,
    # such as update.php/selection. The rule we use is strict, and only allows this pattern
    # with the update.php front controller.  This allows legacy path aliases in the form of
    # blog/index.php/legacy-path to continue to route to Drupal nodes. If you do not have
    # any paths like that, then you might prefer to use a laxer rule, such as:
    #   location ~ \.php(/|$) {
    # The laxer rule will continue to work if Drupal uses this new URL pattern with front
    # controllers other than update.php in a future release.
    location ~ '\.php$|^/update.php' {
        fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
        #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
        include fastcgi_params;
        include snippets/fastcgi-php.conf;
        fastcgi_param SCRIPT_FILENAME $request_filename;
        fastcgi_intercept_errors on;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;

    # Fighting with Styles? This little gem is amazing.
    # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6
    location ~ ^/sites/.*/files/styles/ { # For Drpal >= 7
        try_files $uri @rewrite;

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;


The Drupal virtual host file has been created, now we have to activate it by creating a symlink to the file in the "sites-enabled" directory:

ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/

Test the Nginx configuration and if there are no errors restart Nginx:

nginx -t
systemctl restart nginx

Install MySQL

sudo apt-get install mysql-server
​sudo mysql_secure_installation

Warning: As of July 2022, an error will occur when you run the mysql_secure_installation script without some further configuration. The reason is that this script will attempt to set a password for the installation’s root MySQL account but, by default on Ubuntu installations, this account is not configured to connect using a password.

See: How To Install MySQL on Ubuntu 20.04 for mysql_secure_installation fix

Prior to July 2022, this script would silently fail after attempting to set the root account password and continue on with the rest of the prompts. However, as of this writing, the script will return the following error after you enter and confirm a password:


... Failed! Error: SET PASSWORD has no significance for user 'root'@'localhost' as the authentication method used doesn't store authentication data in the MySQL server. Please consider using ALTER USER instead if you want to change authentication parameters. New password:

This will lead the script into a recursive loop which you can only get out of by closing your terminal window.

Because the mysql_secure_installation script performs a number of other actions that are useful for keeping your MySQL installation secure, it’s still recommended that you run it before you begin using MySQL to manage your data. To avoid entering this recursive loop, though, you’ll need to first adjust how your root MySQL user authenticates.

First, open up the MySQL prompt: 

  1. sudo mysql

Then run the following ALTER USER command to change the root user’s authentication method to one that uses a password. The following example changes the authentication method to mysql_native_password:

  1. ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';

After making this change, exit the MySQL prompt:

  1. exit

Following that, you can run the mysql_secure_installation script without issue.

Install phpMyAdmin

sudo apt-get install phpmyadmin

Hit ESC when the installation prompts you for auto-configuration because there is no option for Nginx.

Make an easily accessible symlink

sudo ln -s /usr/share/phpmyadmin/ /srv/www/

Install and Configure Drupal

Enter the directory that we created earlier and download Drupal with wget. I'm using the latest Drupal 10.0.9. release as of May 3, 2023, make sure you are downloading the latest version by visiting the Drupal download page and writing down the last numbers (version)

cd /srv/www/
sudo wget
sudo tar -xvzf drupal-10.0.9.tar.gz
sudo cp drupal-10.0.9/* public_html/ -R
sudo chown www-data:www-data public_html -R

Now visit your Drupal site in the web Browser, you should see the similiar screen

Drupal 10 installation screen

Now, using phpmyadmin login into MySQL and setup database

Optional: How to edit hosts file on Mac

from the terminal 

sudo nano /etc/hosts

add your IP address pointing to the domain name to test before the DNS zone gets updated

If you need any assistance configuring servers or working with Drupal, consider hiring me at the Terramatris website