Requirements and Hosting
|
This content was generated with the assistance of AI and should be verified against the official Drupal documentation before being relied on in production. This sub-section’s Drupal bibliography lists the reference material consulted while preparing these pages. |
This page was checked on 2026-10-30 against the Drupal 11.4.x line (the version to start a new site on), with Drush 13 or 14 and PHP 8.3 or later. It also lists the numbers for Drupal 10.4 to 10.6 (supported until 2026-12-09, when Drupal 10 reaches end of life) and for Drupal 12, which is scheduled for the week of 2026-12-07 and is therefore still in beta. Do not start a new site on Drupal 10.
It is written for administrators preparing a server and for site owners choosing a host. It lists what Drupal needs (PHP, extensions, memory, database, web server, Composer), where the official sources disagree, and the common hosting types for the Meridian Museum site. Installing the site is covered in Installation with Composer and DDEV, the release cycle in Versions, Release Cycle and Support, and container setups in Docker and Containers.
The requirements at a glance
| Component | Drupal 11.4 (recommended for new sites) | Drupal 12 (scheduled) |
|---|---|---|
PHP |
8.3, 8.4 or 8.5 |
8.5, with argon2 support |
MySQL |
8.0 or later |
8.0 or later |
MariaDB |
10.6 or later |
10.11 or later |
PostgreSQL |
16 or later, with the |
18 or later according to the requirements page (see below) |
SQLite |
3.45 or later |
3.45 or later |
Web server |
Apache 2.4.7 or later, or nginx |
Apache 2.4.7 or later, or nginx |
Composer |
2.7.0 or later |
The latest secure release |
PHP |
At least 64M; 128M to 256M is typical in production |
Same |
The authoritative pages are the System requirements section of the Drupal documentation (PHP, database server, web server, memory, Composer and shared-hosting pages). The User Guide’s server requirements concept page is a shorter overview. Where a number below differs between sources, both values are given.
PHP versions per core minor
Drupal supports a PHP version only for the core minors marked in the PHP requirements page, so the PHP version often decides when a site must upgrade core.
| PHP | 10.4 | 10.5 | 10.6 | 11.1 | 11.2 | 11.3 | 11.4 | 12.0 |
|---|---|---|---|---|---|---|---|---|
8.1 |
yes |
yes |
yes |
no |
no |
no |
no |
no |
8.2 |
yes |
yes |
yes |
no |
no |
no |
no |
no |
8.3 |
yes |
yes |
yes |
yes |
yes |
yes |
yes |
no |
8.4 |
yes |
yes |
yes |
yes |
yes |
yes |
yes |
no |
8.5 |
no |
no |
no |
no |
no |
yes |
yes |
yes |
8.6 |
no |
no |
no |
no |
no |
no |
no |
no |
In prose: Drupal 10.4 to 10.6 run on PHP 8.1 to 8.4; Drupal 11.3 and 11.4 run on PHP 8.3 to 8.5; Drupal 12 requires PHP 8.5. Drupal 10 needs at least PHP 8.1, and 8.1.6 or later is recommended. Drupal 11 needs at least PHP 8.3. PHP 8.6 is not supported yet; the PHP requirements page tracks it in a core issue.
Practical consequences:
-
A site on Drupal 10.6 and PHP 8.1 or 8.2 must raise PHP to 8.3 or later before moving to Drupal 11. Do it in that order: first PHP 8.3 on Drupal 10.6, then core.
-
Drupal 11.3 is the first minor that runs on PHP 8.5, and it is the minimum that a Drupal 12 upgrade accepts, so a site on 11.3 or 11.4 with PHP 8.5 is already on the Drupal 12 platform.
-
Contributed modules and Drush have their own PHP constraints. Check
composer why-not php 8.5before changing the PHP version of a site.
Older books such as Drupal 10 Development Cookbook (2023) and Drupal 10 Masterclass (2023) state PHP 8.1 as the
target, MariaDB 10.3, MySQL 5.7.8, PostgreSQL 12 and SQLite 3.26 as the database minimums, and one book shows a
platform.php value of 8.0.7, which is below the minimum of Drupal 10 itself. Those numbers describe Drupal 10.0 and
10.1; for Drupal 11 they are all too low.
PHP extensions, settings and memory
Drupal core needs these PHP extensions:
-
PDO, with the native extension (the PECL version is not compatible), and one database driver:pdo_mysql(MySQL and MariaDB),pdo_pgsqlorpdo_sqlite -
xml(which bringsdom),jsonandgdfor image styles (ImageMagick is supported through contributed modules) -
mbstringfor installation in non-English languages and for multilingual sites, such as the Meridian Museum site in English and Spanish -
curlfor automated testing and several contributed modules, andopenssl -
argon2support in the PHP build on Drupal 12 (most PHP 8.5 builds have it), because Drupal 12 hashes passwords with argon2id by default
OpenSSL matters more than it looks. The Update Manager fetches update data over HTTPS, so a PHP without OpenSSL, or
without usable CA certificates, produces "Failed to fetch available update data" on the Status report. The guide on
the OpenSSL requirement lists checking connectivity and proxy settings, enabling the extension in php.ini, pointing
curl.cainfo or openssl.cafile at a CA bundle, and, as a last resort, setting
$settings['update_fetch_with_http_fallback' = TRUE;]. That fallback allows plain HTTP and exposes the site to
man-in-the-middle attacks, so it is not for production.
Memory has two numbers:
-
the server needs about 1 GB of RAM in total for Drupal; a Drupal 10 site with about 30 contributed modules peaked near 200 MB during
composer update, and the documentation recommends 1 GB free for Composer to be safe -
the PHP
memory_limithas a floor of 64M (Drupal warns below it), and 128M or 256M is typical in production. Media-heavy sites such as a museum image archive, Search API indexers and Drush batch commands need more
A php.ini fragment for a production web pool:
; /etc/php/8.4/fpm/conf.d/90-meridian.ini
memory_limit = 256M
upload_max_filesize = 32M
post_max_size = 40M
max_execution_time = 60
; Drupal 10 and 11 read plugin metadata from PHP attributes and annotations.
opcache.enable = 1
opcache.memory_consumption = 192
opcache.max_accelerated_files = 20000
opcache.save_comments = 1
; Xdebug (development machines only)
; xdebug.max_nesting_level = 256
Source: PHP requirements
opcache.save_comments must stay at 1 (the default): with the comments stripped, annotation-based plugin discovery
stops working, and annotations are still used by older contributed modules. On development machines, Xdebug needs
xdebug.max_nesting_level = 256 on versions before 2.3 and xdebug.show_exception_trace = 0 so that the installer
does not crash. The PHP command-line binary that runs Drush and Composer reads a different php.ini than the web pool
(php --ini shows which); give it a higher or unlimited memory_limit for long batch operations. General PHP
configuration is covered in Configuration and
Extensions and OPcache in OPcache, JIT and
Performance.
Databases per major
Drupal 10, 11 and 12 differ in database minimums:
| Database | Drupal 10 | Drupal 11 | Drupal 12 |
|---|---|---|---|
MySQL |
5.7.8 or later |
8.0 or later |
8.0 or later |
MariaDB |
10.3.7 or later |
10.6 or later |
10.11 or later |
PostgreSQL |
12 or later |
16 or later |
18 or later (requirements page) |
SQLite |
3.26 or later |
3.45 or later |
3.45 or later |
Notes that apply to each database:
-
MySQL and MariaDB need InnoDB as the primary storage engine and the PDO extension. Both are supported by the same driver. Some contributed modules contain MySQL-specific code, which is reported in the module’s own issue queue.
-
PostgreSQL needs the
pg_trgmextension in all three majors. Create it as a sufficiently privileged user while connected to the Drupal database itself; creating it in another database on the same server does not help. -
SQLite is meant for small sites, tests and demonstrations. Drupal 10 additionally needs the
json1extension. -
Microsoft SQL Server and MongoDB are available through contributed modules only.
The Drupal 12 PostgreSQL discrepancy. The database requirements page lists PostgreSQL 18 or later for Drupal 12. The
12.0.0 alpha and beta 1 release notes state a different floor (19 is named there as the target, with 18 the current
stable version), and the 12.0.0-beta2 installer check and INSTALL.txt are reported to require PostgreSQL 16. Until
the final 12.0.0 release settles it, a site that must run on PostgreSQL should plan for 18 and treat anything older as
unconfirmed. MariaDB 10.11 is the Drupal 12 minimum in the same sources; PHP 8.5 is required with argon2.
Create the Meridian Museum database on MariaDB with a UTF-8 character set (the placeholder password is replaced by a value from the environment):
mariadb -u root -p <<'SQL'
CREATE DATABASE meridian CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'meridian'@'localhost' IDENTIFIED BY 'CHANGE_ME';
GRANT ALL PRIVILEGES ON meridian.* TO 'meridian'@'localhost';
FLUSH PRIVILEGES;
SQL
Source: Database server requirements
For PostgreSQL the equivalent step is the extension, run inside the database:
CREATE EXTENSION IF NOT EXISTS pg_trgm;
Source: Database server requirements
Transaction isolation
On MySQL and MariaDB, Drupal recommends the READ COMMITTED transaction isolation level. The server default,
REPEATABLE READ, is supported but can cause table deadlocks that slow the site or make it unresponsive; the Status
report (/admin/reports/status) warns when it detects it. READ UNCOMMITTED and SERIALIZABLE are unsupported. Set
the level on the server rather than in Drupal, because the settings.php option runs a statement on every request:
# /etc/mysql/conf.d/meridian.cnf
[mysqld]
transaction_isolation = "READ-COMMITTED"
// settings.php: works, but costs one extra query per request.
$databases['default']['default']['isolation_level'] = 'READ COMMITTED';
READ COMMITTED supports only row-based binary logging, so a server that used mixed logging switches to row logging,
which needs more storage. On MariaDB and on MySQL 5.7.19 and earlier the setting is named tx_isolation.
Web servers
Drupal runs on Apache and nginx, on Linux, Unix, macOS and Windows.
-
Apache must be 2.4.7 or later.
mod_rewriteis required for clean URLs,mod_headersfor serving gzip-compressed CSS and JavaScript, and the virtual host needsAllowOverride Allso that Drupal’s.htaccessis honoured. When MultiViews is on by default, add-MultiViewstoOptions. The.htaccessshipped with core also blocks access to private files and sets the PHP flags, which only apply when PHP runs as an Apache module; with PHP-FPM those settings go in the pool configuration. The 11.4.11 and 10.6.20 versions of that file replace the removedassert.activesetting withzend.assertions. -
nginx is supported from a very old 1.1, and any current release works. Because
.htaccessis Apache-only, every protection in it (denying.yml,.twigand other source files, blocking PHP in the files directory, denying private files) must be recreated in the server block. -
IIS: as of Drupal 11.0 Drupal does not support Microsoft IIS. Sites on IIS must move to Apache or nginx before upgrading from Drupal 10.
-
Windows: running Drupal directly on Windows in production is deprecated from 11.3 because there is no automated Windows test environment and few developers who can test on it. Use Linux, or Linux on Windows through WSL or containers; Windows remains fine for development (DDEV runs on it through WSL2).
-
The PHP built-in web server (
php -S) is for development and testing only.
A virtual host for the production site:
<VirtualHost *:443>
ServerName www.meridian-museum.example
DocumentRoot /var/www/meridian/web
<Directory /var/www/meridian/web>
AllowOverride All
Options -Indexes -MultiViews +FollowSymLinks
Require all granted
</Directory>
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
</FilesMatch>
SSLEngine on
SSLCertificateFile /etc/ssl/certs/meridian.pem
SSLCertificateKeyFile /etc/ssl/private/meridian.key
</VirtualHost>
Source: Web server requirements
The same site on nginx, with the protections that .htaccess provides on Apache:
server {
listen 443 ssl;
server_name www.meridian-museum.example;
root /var/www/meridian/web;
index index.php;
location ~ \..*/.*\.php$ { return 403; }
location ~ ^/sites/.*/private/ { return 403; }
location ~ (^|/)\. { return 403; }
location / {
try_files $uri /index.php?$query_string;
}
location ~ '\.php$|^/update.php' {
fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
location ~ ^/sites/.*/files/styles/ {
try_files $uri @rewrite;
}
location @rewrite {
rewrite ^ /index.php;
}
}
Source: Web server requirements
HTMX headers behind proxies and CDNs (11.3 and later)
Drupal 11.3 added built-in HTMX support, which drives parts of the core UI with HTTP headers. Any reverse proxy, CDN
or web application firewall in front of the site must pass them unchanged, otherwise dynamic parts of the interface
stop working. The documented request headers are HX-Boosted, HX-Current-URL, HX-History-Restore-Request,
HX-Prompt, HX-Request, HX-Target, HX-Trigger-Name and HX-Trigger. The response headers are HX-Location,
HX-Push-Url, HX-Redirect, HX-Refresh, HX-Replace-Url, HX-Reswap, HX-Retarget, HX-Reselect, HX-Trigger,
HX-Trigger-After-Settle and HX-Trigger-After-Swap. A strict header allow-list on a CDN is the usual cause of
"works locally, breaks behind the CDN" reports. The Ajax and HTMX layer is covered in
AJAX and HTMX.
Composer
Composer is the only supported way to install and update Drupal code. The minimum is 2.3.6 for Drupal 10 (needed by Automatic Updates), 2.7.0 for Drupal 11 (because of a February 2024 security fix), and the latest release for Drupal 12. Run Composer with the same PHP version as the web server, and give the process enough memory; Composer is the most memory-hungry step of a deployment. Pin the platform so that a developer’s newer PHP cannot resolve packages the production PHP cannot run:
{
"name": "meridian/museum-site",
"type": "project",
"require": {
"php": ">=8.3",
"drupal/core-recommended": "^11.4",
"drupal/core-composer-scaffold": "^11.4",
"drush/drush": "^13"
},
"config": {
"platform": { "php": "8.3.0" },
"sort-packages": true
}
}
Source: Composer requirements
Uploading a module or theme from the UI was removed in Drupal 10.4 and 11.0, and updating from the UI in 11.2; the
Package Manager behind Automatic Updates and Project Browser still needs a writable codebase, which affects hosting
choice (see the next section). drupal/legacy-project is abandoned as of 11.4, so use drupal/recommended-project.
Where the code lives: the web root
With drupal/recommended-project the public folder is web/, and vendor/, composer.json and the configuration
sync directory sit above it. The host must therefore point the document root at web/.
Relocated web root on shared hosting
Many shared plans serve a fixed public_html folder that cannot be changed. The Drupal documentation lists four
approaches, from cleanest to least recommended:
-
Set the document root to
web/in the control panel (cPanel addon domains, Plesk hosting settings, Hetzner’s konsoleH). Only an administrator can change the primary domain’s root in cPanel, so ask the host. -
Redirect with
.htaccessinpublic_html. This does not work during installation, emergency rebuilds or UI package downloads. For Drupal 11.3 and earlier the rewrite is combined with aSCRIPT_NAMEfix at the end ofsettings.php. For Drupal 11.4 that workaround breaks: the documented replacement sets aDRUPAL_WEBROOTenvironment variable and adds a block toweb/autoload_runtime.phpthat strips the prefix fromSCRIPT_NAME. The page marks the 11.4 recipe as not thoroughly tested. -
Use a subdomain whose document root is
web/, with a redirect from the main domain. -
Symlink
public_htmltoweb/, which needs SSH and is not recommended: during maintenance a URL such as/public_html/core/can expose the folder the symlink points to.
An outline of the redirecting .htaccess for Drupal 11.4 (the documented block in web/autoload_runtime.php is also
required):
# public_html/.htaccess
RewriteEngine On
RewriteRule .* - [E=DRUPAL_WEBROOT:/web]
RewriteCond %{REQUEST_URI} !^/web/
RewriteRule ^(.*)$ /web/$1 [L]
Because the shared-hosting recipes alter Drupal’s entry scripts and cannot be used for installs, a site that matters
should not rely on them. The security consequences of exposing vendor/ or composer.json are covered in
Security Hardening.
Choosing a hosting type
| Type | Fits | Limits |
|---|---|---|
Shared hosting |
Small brochure sites, a tight budget |
No SSH or Composer on many plans, fixed document root, low PHP memory, no Drush, hard to keep a writable codebase; Meridian’s |
VPS or dedicated server |
Full control: PHP-FPM, Redis, Varnish, cron, Drush, Composer deployment |
You patch the operating system, PHP and the database, and monitor the server |
Managed Drupal platform |
Drupal-specific hosts and PaaS (Acquia, Pantheon, Platform.sh/Upsun, Amazee.io and others): Composer builds, environments per branch, backups, CDN, security patching of the stack |
Cost; the build process and read-only code file system dictate how deployments and Automatic Updates work |
Containers |
Reproducible stacks with Docker, Compose or Kubernetes: the same image from DDEV to production |
You own the orchestration; files, sessions and cron need shared services |
Evaluate a host against the minimums above: a PHP version matching your core minor, the database version, Composer
and SSH access (or a build pipeline), a cron that can run every few minutes, enough memory_limit, HTTPS, backups,
and a way to deploy settings.php and environment secrets without storing them in Git. The shared-host document root
problem and the HTMX header allow-list are the two issues that most often surprise teams. Container images are
explained in Docker getting started and
Compose for local development.
For local development, DDEV reproduces the production stack with one file:
# .ddev/config.yaml
name: meridian
type: drupal11
docroot: web
php_version: "8.4"
webserver_type: nginx-fpm
database:
type: mariadb
version: "10.11"
composer_version: "2"
Source: DDEV quickstart: Drupal
Common errors and pitfalls
-
The installer reports "Your PHP version is too low" because the command-line PHP and the web PHP differ. Compare
php -vwithphpinfo(). -
"Failed to fetch available update data" is a missing OpenSSL extension, a broken CA bundle or a blocked outbound connection, not a Drupal bug.
-
The Status report shows REPEATABLE READ: change the server setting as described above.
-
PostgreSQL install fails because
pg_trgmwas created in thepostgresdatabase instead of the Drupal one. -
Clean URLs fail on Apache because
mod_rewriteis off orAllowOverrideisNone. -
The
.htaccessis absent after copying a project (hidden file), so the site appears to work but is unprotected. -
A CDN strips HTMX headers and parts of the 11.3 or later UI stop updating.
-
A site on IIS or on Windows in production cannot follow core beyond the deprecation; plan the move to Linux.