Install Drupal 10 content management system

Go to End

-> Top

1. Note

  • 8/11/2023: TOP API and Convert Bundles installations added. Adding buttons to CKEditor revised.
  • 5/11/2023: Upgrading to Drupal 10 added. Drupal 9 and before deleted.
  • 21/12/2021: Bartik theme used instead of Mayo.
  • 11/11/2021: Errors in updating core manually corrected.
  • 5/1/2020:  Problems when updating to Drupal 8.8 under php7.4 described. Some text updating.
  • 9/9/2019: Composer files also copied when relocating system.
  • 18/7/2019: Composer configuration actions added.
  • 7/7/2019: Original user-defined files and attributes kept when updating manually.
  • 26/5/2019: Composer and Drush installations added. Manual core installation added. Deleting configuration file added.
  • 16/5/2019: Simplified installation step added. Using new shell to restore added.
  • 8/5/2019: Page on Drupal 8 withdrawn.  All "/var/www/web" changed to "/var/www/html/web" because Apache2 prefers to put website files underneath "/www/web/html", and it is easier that way. Relocating Drupal 8 added. Print page stylesheet re-written.
  • 18/1/2019: Updating Drupal 8 core using composer added.
  • 30/9/2018: Setting CKEditor to use Mayo theme stylesheet added. Setting print page to also use Mayo theme stylesheet added. Adding custom styles added.
  • 23/9/2018: "php/7.0" changed to "php/7.2"
  • 3/9/2018: First created from the page on Drupal 7 after major upgrading to Drupal 8. Resolution of CKEditor table border added.
-> Top

2. Intro

(section revised, 5/11/2023)

Drupal is a web site content management system serving web pages to the internet.

Upgrading from Drupal 9 to 10 has been deferred because Drupal 10 has changed the default theme Bartik to Olivero front-end theme and Claro administration theme and some of the modules do not have compatible upgrades. We have been using Bartik and prefer it over those new themes.

However, Drupal 9 reached end of life on 1 Nov 2023. Our Drupal 9.5.11 was therefore upgraded to Drupal 10.1.6 on 4 November 2023. This page documents the upgrading procedures. Most of the previous text about installing Drupal 8 and upgrading to Drupal 9 has been deleted because they are not useful anymore. Some old bits and pieces have been retained in case they may be useful.

-> Top

3. Install Drupal 10

(section added, 5/11/2023)

Go to Drupal's website and go to the web page of the latest release.

The webpage for Drupal 10.1.6 is

According to it, to start a new Drupal project:

$ cd /var/www/html/
$ composer create-project drupal/recommended-project:10.1.6 "install-dir"

Change "install-dir" to a new name such as "web".

To update an existing Drupal site and all dependencies to the latest release of Drupal:

$ cd /var/www/html/web
$ composer update "drupal/core-*" --with-all-dependencies

To update an existing Drupal site to this specific release:

$ cd /var/www/html/web
$ composer require drupal/core-recommended:10.1.6 drupal/core-composer-scaffold:10.1.6 drupal/core-project-message:10.1.6 --update-with-all-dependencies
-> Top

4. Upgrade Drupal 9 to 10

(section added, 5 Nov 2023)

The upgrading command as given above is very simple, but the reality is not that simple because there can be many errors due to incompatible modules and themes as well as locking to some previous releases, resulting in the inability to upgrade. There are many other websites describing how to solve the upgrading problems, but the solutions mostly cannot work. After about 12 hours of trials and errors  over two days, the upgrading was finally successful. The following records the procedures used (based on memory).

Go to Drupal's top folder and install drupal/upgrade_status:

$ cd /var/www/html/web
$ composer require drupal/upgrade_status

Go to Manage > Reports > Upgrade status.

Get the list of modules and themes which should be updated for Drupal 10.

Get the list of modules and themes which are incompatible with Drupal 10 or no longer used.

Go to Manage > Extend or > Appearance to update or remove modules and themes.

Alternatively, use composer to update or remove:

$ composer require <group name>/<name>
$ composer require <group name>/<name>:<add version number as necessary>
$ composer remove <group name>/<name>

Go to Manage > Configuration > Text formats and editors to change the text editor from CKEditor to CKEditor 5.

Change back to CKEditor if errors occur when saving the change. Remove those not acceptable menu icons. Change back to CKEditor 5 until the change is successfully saved.

Remove drupal/upgrade_status because the installed version is not compatible with Drupal 10. 

$ composer remove drupal/upgrade_status

Update all files and settings to the current version:

$ composer update --with-all-dependencies
$ vendor/drush/drush/drush updatedb

"--with-all-dependencies" can be abbreviated as "-W".

Enable write access (not tested whether this is really necessary):

chmod 777 web/sites/default
chmod 666 web/sites/default/*settings.php
chmod 666 web/sites/default/*services.yml

Install Drupal 10 but without updating, and edit composer.json (see alternative below):

$ composer require drupal/core-recommended:10.1.6 drupal/core-composer-scaffold:10.1.6 drupal/core-project-message:10.1.6 --no-update
$ nano composer.json

Change the following line to state the new release number (it is an important step in this order, otherwise, upgrading will report incompatible new release or locked old releases):

"drupal/core": "10.1.6",

Upgrade now based on the newly specified release:

$ composer update --with-all-dependencies

Alternatively, the following command may work instead of  the above command with "--no-update" option and the update command:

$ composer require "drupal/core:9.5.11 as 10.1.16" --no-update && composer update

However, the following line in composer.json still needs to be changed as such afterward.

"drupal/core": "10.1.6",

Update drush and update the database

$ composer require drush/drush
$ vendor/drush/drush/drush updatedb

The updatedb command may report post-update changes. Generally answer "Yes" to accept the changes.

The upgrading should be successful.

Restore read-only access:

chmod 755 web/sites/default
chmod 644 web/sites/default/*settings.php
chmod 644 web/sites/default/*services.yml

Install drupal/bartik as a contributed theme:

$ composer require drupal/bartik

Go to Manage > Appearance to make it the default theme.

Add back any compatible modules previously deleted due to incompatibility.

Go to Manage > Reports > Status report to see what errors arise.

It may remind to download colorbox-master. Download and extract it to a different name as /var/www/html/web/libraries/colorbox.

It may also remind to download DOMPurify-main. Download and extract it. Move its "dist" directory to become /var/www/html/web/libraries/dompurify/dist.

Some modules are related to CKEditor, they have to be removed before CKEditor can be removed. Some of them can be re-installed after the removal of CKEditor and automatically linked to CKEditor 5.

-> Top

5. Add TOC API for table of contents

(section added, 8/11/2023)

Unlike CKEditor 4, CKEditor 5 does not have table of contents for free. The previous tables of contents have to be removed page by page.

Use TOC API instead.

Go to Drupal's top folder and install drupal/toc_api:

$ cd /var/www/html/web
$ composer require drupal/toc_api

TOP API Example will automatically be installed also. TOP API is the back end. The Example actually adds the table of contents.

Enable the two modules at Manage > Extend > List.

Go to Manage > Structure > Table of contents types to see a list of example tables.

The default is the one that will be used. The other are examples.

Table of contents types

 Edit the default. Use responsive to suit mobile phones, which will show on mobile phones the title only with a drop down menu.

General settings

Change back to top maximum level to "h2". Change "Back to top" to "-> Top".

Back to top settings

No change here.

Header settings

Change numbering suffix from ") " to ". " both ending with a space.

The numbering suffice follows the numbers to the headings in the body. The numbering separator follows the numbers in the table of contents.

Numbering settings

-> Top

6. Install Convert Bundles

(section added, 8/11/2023)

When testing various table of contents modules, it was found that some would apply to the Basic Pages but not the Book Pages. This website previously used mainly Book Pages. The drupal/convert_bundles module can be used to convert a type of contents pages to another type. Generally, follow the default settings. After many trials, TOC API has been chosen for the table of contents. By that time all the Book Pages have been changed to Basic Pages. It has not been tested whether the conversion is really required in order to use TOC API.

-> Top

7. Prepare for a newly installed Drupal

(section revised, 5/11/2023)

Install Apache2 web server if not already installed.

Enable Apache2 rewrite module (should have been automatically installed when installing HTTPS):

$ sudo a2enmod rewrite

Configure Apache2:

$ sudo gedit /etc/apache2/apache2.conf

Specify, keeping "<" and ">":

<Directory /var/www/>
    Options Indexes FollowSymLinks
    # AllowOverride None # replaced with next line
    AllowOverride All
    Require all granted

Restart Apache2 service:

$ sudo systemctl restart apache2
$ sudo service apache2 restart

Install MySQL server + PHP5 + phpMyAdmin, and create a user with database both called "drupal8". (This old name has not been changed since its creation.)

Follow the instructions on Drupal webpages to set up the rest.

The following bits and pieces were done for Drupal 9. Not sure whether they are the same now since new installation has been done.

  • Change ownership:
$ sudo chown www-data:www-data sites/default/settings.php
  • Choose language: English language
  • Choose profile: Standard
  • Setup database: MySQL
  • MySQL database name, user name and password as defined above
  • Site name:
  • Site e-mail address: <>
  • Site maintenance username: <name of the administrator>
  • Site maintenance user e-mail address: <>
  • Default country: Hong Kong S.A.R., China
  • Default time zone: Asia/Hong Kong
  • Check for updates automatically
  • Receive e-mail notifications
  • Change directory permissions:
$ sudo chmod 555 sites/default
$ sudo chmod 444 sites/default/settings.php
  • Permit uploading of files:
$ sudo chown www-data:www-data -R sites/default/files
  • Increase upload file size limit (php 8.2 used now):
$ sudo gedit /etc/php/8.2/apache2/php.ini
  • Change existing to:
post_max_size = 200M
upload_max_filesize = 50M
max_file_uploads = 100
  • Restart Apache2 service after every change:
$ sudo systemctl restart apache2
$ sudo service apache2 restart
  • Edit "settings.php" file:
$ sudo nano /var/www/html/web/sites/default/settings.php
  • Specify trusted hosts:
$settings['trusted_host_patterns'] = array(
  • Disable "update.php" file:
$ sudo mv /var/www/html/web/update.php /var/www/web/<some new name>
-> Top

8. Re-direct http path to sub-directory "web"

The default DocumentRoot  in the default enabled Apache2 configuration file /etc/apache2/sites-enabled/000-default.conf  is "/var/www/html/".

Web browers can access any permissible directories or files underneath the DocumentRoot. Access outside the Document Root will not be possible.

When the Drupal root directory /web is placed underneath /var/www/html such as /var/www/html/web, then will access the default index.html file underneath /var/www/html, but in the case of, access to those underneath /web is possible.

To re-direct to access /var/www/html/web directly, create a new ".htaccess" file under "/var/www/html":

$ sudo gedit /var/www/html/.htaccess


<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteBase /
  RewriteRule ^(.*)$ web/$1 [R]

"^(.*)$" represents the full text from start to end after the domain name, e.g. "abc/def" in "".

"web/$1" represents the substitution text where "$1" represents the text represented by "(.*)" with "web/" inserted before it, i.e. "web/abc/def" based on the above example. This would redirect the path to "web/abc/def".

"[R]" is to redirect the path and will show "web/" as part of the redirected path. When using "[R]", "RewriteBase /" is required to add "/" before "web/$1". Without this, "/var/www/html" will be added before "web/$1" and will cause unexpected results. This effect was discovered after many days of error discovery using "[L]".

Using [L] as suggested by many people may hide "web/" from the displayed path, and does not require the use of "RewriteBase", but some of the web pages will still show "web/" unavoidably. A mixed use with or without "web/" displayed will cause denial of access rights or redirection to external URL in some cases. Therefore, it is better to force to display "web/" using "[R]".

The following has the same effect:

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteBase /web
  RewriteRule ^(.*)$ $1 [R]
-> Top

9. Remove "www." prefix from URL

This is not essential for using Drupal, but is adopted only to simplify.

Insert the following in /var/www/html/.htaccess after the RewriteBase line:

# Remove "www." prefix from URL
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
-> Top

10. Define Bartik theme color scheme

(section revised, 5/11/2023)

Log in as an administrator if not already in.

Choose Manage > Appearance > Settings, and define Bartik color scheme as follows:


Configure Bartik's css files as described below to match the preferences of this website.

-> Top

11. Add custom styles to Bartik theme

(section added, 21/12/2021)

(section revised, 6/11/2023)

Log in the server.

Create a "misc" directory under the Drupal root directory, and edit to create 4 new files to re-define webpage styles:

$ cd /var/www/html/web
$ mkdir misc
$ cd misc
$ nano <new file name>

Create "append-to-elements.css" file to contain:

/* the following settings added, KCTang 18/12/2021 */
body {
 margin: 0;
 color: blue;
 font-family: Arial;
p {
 margin: 0.2em 0 0.2em;
 border: 1px solid green;
 font-family: monospace;
 margin: 0.15em 0.2em 0.15em;
 padding: 0.2em;
 background-color: #f8f9fa;
 white-space: pre-wrap;
 display: table;
 border: 1px solid green;
 background-color: #f8f9fa;
 padding: 0.5em;
 font-size: 0.9em;
.hangtwice {
 margin-left: 80px;
 text-indent: -80px;
.hang1 {
 margin-left: 40px;
 text-indent: -40px;
.hang2 {
 margin-left: 80px;
 text-indent: -40px;
.hang3 {
 margin-left: 120px;
 text-indent: -40px;
.hang4 {
 margin-left: 160px;
 text-indent: -40px;
.hang5 {
 margin-left: 200px;
 text-indent: -40px;
.indent1 {
 margin-left: 40px;
.indent2 {
 margin-left: 80px;
.indent3 {
 margin-left: 120px;
.indent4 {
 margin-left: 160px;
.heading-a {
 color: blue;
 margin: 0 0 0.5em;
h2 {
 border: 1px solid;
#  border-bottom: 1px solid;
 color: red;
 text-align: center
h2.hang {
 border: 1px solid;
 margin-left: 40px;
 text-indent: -40px;
 color: red;
h3 {
 border-bottom: 1px solid;
 font-weight: bold;
 font-weight: normal;
 color: magenta;
h3.hang {
 margin-left: 40px;
 text-indent: -40px;
 font-weight: normal;
 color: magenta;
h4.hang {
 margin-left: 40px;
 text-indent: -40px;
a, {
 border-bottom: none;
.site-branding__name {
 font-family: "Times New Roman";
img {
 margin: 15px;
/* the following settings added, KCTang 11/2/2023 */
blockquote {
 border-left: 2px solid #bbb; /* LTR */
 background: #F7F84D;
blockquote:before {
 margin-right: 0;
 content: "";
[dir="rtl"] blockquote:before {
 content: "";
blockquote:after {
 content: "";
[dir="rtl"] blockquote:after {
 content: "";
/* the following settings added, KCTang 22/3/2023 */
 font-family: math;

Create "append-to-layout.css" file to contain:

/* the following settings added, KCTang 18/12/2021 */
@media all and (min-width: 851px) {
 .layout-container {
   max-width: 1920px;

Create "append-to-main-content.css" file to contain:

/* the following settings added, KCTang 18/12/2021*/
.main-content h2 {
 margin: 0.5em 0;
 font-size: 1.429em;

Create "append-to-text-formatted.css" file to contain:

/* the following settings added, KCTang 18/12/2021*/
.text-formatted ul,
.text-formatted ol {
 margin: 0;
 padding: 0 0 0.25em 15px; /* LTR */

Change to /var/www/html/web directory.

Create "" file to contain:

cat /var/www/html/web/misc/append-to-elements.css >> /var/www/html/web/themes/contrib/bartik/css/base/elements.css
cat /var/www/html/web/misc/append-to-layout.css >> /var/www/html/web/themes/contrib/bartik/css/layout.css
cat /var/www/html/web/misc/append-to-main-content.css >> /var/www/html/web/themes/contrib/bartik/css/components/main-content.css
cat /var/www/html/web/misc/append-to-text-formatted.css >> /var/www/html/web/themes/contrib/bartik/css/components/text-formatted.css
ls -ls /var/www/html/web/themes/contrib/bartik/css

This file is to append the settings in the last 4 files to Bartik's css files to override Bartik's settings.

Execute to append whenever there is an update to Bartik:

$ cd /var/www/html/web
$ ./

Clear cache to reveal the effects of the new settings:

$ vendor/drush/drush/drush cr
-> Top

12. Add buttons to CKEditor 5

(revised, 8/11/2023)

Log in the website as an administrator.

Choose Manage > Configuration > Text formats and editors > Configure Full HTML.

Move the required buttons down to a suitable position in the active toolbar:

Active toolbar

Include moving the Style button.

Enter a list of classes and titles as follows:

p.hangtwice|Hang Twice
p.hang1|Hang 1 
p.hang2|Hang 2
p.hang3|Hang 3
p.hang4|Hang 4
p.hang5|Hang 5
p.indent1|Indent 1
p.indent2|Indent 2
p.indent3|Indent 3
p.indent4|Indent 4

"p" stands for paragraph.

".hangtwice" stands for the name of class as defined in the "css/style.css" file.

"Hang Twice" stands for the title seen when the Styles button is pressed to give a drop down menu. 

-> Top

13. Install Composer and Drush

(section added, 26/5/2019) website describes various methods of installing Composer and Drush, generally referring to other websites for complicated steps which are difficult to follow.

The following simple steps do work.


$ sudo apt install composer
$ cd /var/www/html/web
$ composer require drush/drush

Drush is installed under /var/www/html/web/vendor/drush/drush.

-> Top

14. Update Drupal using Composer

(section added, 18/1/2019)

(simplified, 5/11/2023)

Go to the root directory of the website:

$ cd /var/www/html/web

Check for outdated modules:

$ composer show --outdated drupal/* -vvv

"-vvv" is to display what is going on.

Update all:

$ composer update drupal ---vvv

Update database:

$ vendor/drush/drush/drush updatedb -vvv

Answer "yes" when asked to run specified post-update changes.

Clear cache:

$ vendor/drush/drush/drush cr -vvv

Browse the website to see that the webpages can show up successfully.

Set to release the website from maintenance mode.

To copy sub-directories from one directory to another while keeping the original attributes:

$ sudo cp -Rav <old directory>/<name> <new directory>
-> Top

15. Relocate or restore Drupal 8

(section added, 8/5/2019)

(revised, 16/5/2019)

Export the relevant MySQL database using phpMyAdmin as a backup.

Backup the whole Drupal directories and files. For example, if Drupal's root directory is /web in /var/www/html/web, then everything from and below /web.

Use old pair of backups if fresh pair not available.

Create a new user and blank database only if new computer or new MySQL-server is used. 

Import the database backup.

Move the whole Drupal directories and files to the new directory, e.g. /var/www/new/web.

Keep "new" as the original name in case of no change, e.g. /var/www/html/web.

Keep the name "/web" unchanged since this is hard-coded in the beginning of links  in the form of "/web/..." to media and files embedded in the web pages.


$ sudo gedit /var/www/new/web/sites/default/settings.php


$database['default']['default'] = array (
    'database' = '<text>'
    'username' = '<text>'
    'password' = '<text>'

change the <text> to match the new database.

Change the DocumentRoot in the new site's currently used Apache2 site configuration file, e.g. /etc/apache2/sites-enabled/000-default.conf,  from "/var/www/html/" to /var/www/new, still one level above /web. Links in the form of "/web/..." to media and files will be interpreted as "/var/www/new/web/...", still accessible.

Web browsing to should be able to access Drupal at the new location.

Go to the next section in case web browsing fails to display.

If the DocumentRoot is set directly to the Drupal root directory "/var/www/new/web, web browsing of Drupal web pages is still possible, but links to media and files will be interpreted as "/var/www/new/web/web/... ", and the media and files will not be displayed.

Move the ".htaccess" file from /var/www/html to /var/www/new to re-direct web browsing to

$ sudo mv /var/www/html/.htaccess /var/www/new

Execute as necessary:

$ vendor/drush/drush/drush updatedb -vvv
$ vendor/drush/drush/drush cr -vvv

("vendor/drush/drush/" added, 26/5/2019)

-> Top

16. Use new shell to relocate and restore

(section added, 16/5/2019)

(revised, 26/5/2019)

If the relocation or restoration is not successful, non-display may happen because of some corruptions in the file settings.

Follow the Drupal core manual installation steps described above, but substitute "/var/www/html" described there with "/var/www/new".

Setting the website to maintenance mode would not be possible.

Before changing the settings file, move the ".htaccess" file from /var/www/html to /var/www/new:

$ sudo mv /var/www/html/.htaccess /var/www/new
-> Top

17. Delete left-over module configuration files

(section added, 26/5/2019)

If a left-over configuration setting file is reported when a module is re-installed, execute to delete it:

$ cd /var/www/html/web
$ vendor/drush/drush/drush config:delete '<name of configuration file to delete>'


End of Page


-> Top

Back to top with progress scrollbar