Running Drupal on Linode means building a LAMP or LEMP stack on an Ubuntu 24.04 instance, installing Drupal with Composer rather than a downloaded archive, and pointing the web server at the web subdirectory that Composer creates. Drupal 11 requires PHP 8.3 or newer, which Ubuntu 24.04 ships by default, so the version alignment works out neatly. Budget a 2 GB instance at $12 per month for a real site, or $5 for a development box.
composer create-project drupal/recommended-project mysite. Point the document root at mysite/web, create the database, install Drush with composer require drush/drush, and finish with drush site:install. Add a Let’s Encrypt certificate and a cron entry running drush cron every fifteen minutes.What follows is the full sequence: instance sizing, stack install, the Composer project layout, settings and permissions, Drush, cron, TLS and the mistakes that cost people an afternoon.
Sizing the Linode instance
Drupal is heavier than a static site but lighter than most people fear. The dominant costs are PHP FPM worker memory and the database. Akamai’s shared CPU plans map onto that well.
| Plan | Specs | Price | Suits |
|---|---|---|---|
| Nanode 1 GB | 1 GB RAM, 1 CPU, 25 GB | $5 per month | Development, staging, low traffic brochure sites |
| Linode 2 GB | 2 GB RAM, 1 CPU, 50 GB | $12 per month | Most small production Drupal sites |
| Linode 4 GB | 4 GB RAM, 2 CPU, 80 GB | $24 per month | Content heavy sites, many authenticated users |
| Linode 8 GB | 8 GB RAM, 4 CPU, 160 GB | $48 per month | Commerce, media libraries, Solr on the same box |
Choose Ubuntu 24.04 LTS as the image. It carries PHP 8.3 in the default repositories, which satisfies Drupal 11’s requirement without adding a third party PPA. Add a 2 GB swap file on the 1 GB plan so Composer does not get killed during dependency resolution.
Build the stack
Log in over SSH as a user with sudo, update the system, then install Nginx, MariaDB, PHP FPM and the extensions Drupal needs. The list below covers a stock install; add php-imagick if you use advanced image styles.
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx mariadb-server git unzip curl \
php8.3-fpm php8.3-cli php8.3-mysql php8.3-gd php8.3-xml \
php8.3-mbstring php8.3-curl php8.3-zip php8.3-intl php8.3-opcache
sudo mysql_secure_installationCreate the database and a user scoped to it. Never let Drupal connect as root.
sudo mysql -e "CREATE DATABASE drupal CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
sudo mysql -e "CREATE USER 'drupaluser'@'localhost' IDENTIFIED BY 'a-strong-password';"
sudo mysql -e "GRANT ALL PRIVILEGES ON drupal.* TO 'drupaluser'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"Install Composer globally, because the tarball install path is no longer how Drupal is meant to be managed.
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
composer --versionInstall Drupal with Composer
The recommended project template gives you a sensible layout: dependencies in vendor, the public site in web, and a composer.json that tracks core and every contributed module you add. That file is what makes updates and rollbacks possible.
cd /var/www
sudo composer create-project drupal/recommended-project mysite
cd mysite
sudo composer require drush/drush
sudo chown -R www-data:www-data /var/www/mysiteNow install the site itself. Drush does it in one command, which is faster and more repeatable than clicking through the browser installer.
cd /var/www/mysite
sudo -u www-data ./vendor/bin/drush site:install standard \
--db-url=mysql://drupaluser:a-strong-password@localhost/drupal \
--site-name="My Drupal Site" \
--account-name=admin \
--account-pass=change-this-now \
-ydrush user:password admin, and clear the history entry.Nginx, permissions and settings.php
The single most common Drupal on Linode mistake is pointing the document root at the project directory instead of web. Do that and visitors can fetch composer.json, vendor and anything else in the tree. The root must be the web subdirectory.
server {
listen 80;
server_name example.com www.example.com;
root /var/www/mysite/web;
index index.php;
location / {
try_files $uri /index.php?$query_string;
}
location @rewrite {
rewrite ^ /index.php;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ ^/sites/.*/files/styles/ {
try_files $uri @rewrite;
}
location ~ (^|/)\. { return 403; }
location ~ /vendor/.*\.php$ { deny all; return 404; }
client_max_body_size 64M;
}Permissions come next. Drupal writes to the files directory and must not be able to write to settings.php once installation is finished.
cd /var/www/mysite/web/sites/default
sudo chmod 444 settings.php
sudo chmod 755 .
sudo mkdir -p files
sudo chown -R www-data:www-data files
sudo chmod -R 755 filesAdd the trusted host pattern to settings.php so Drupal rejects requests with a forged Host header, and move the config sync directory outside the web root.
$settings['trusted_host_patterns'] = ['^example\.com$', '^www\.example\.com$'];
$settings['config_sync_directory'] = '../config/sync';
$settings['file_private_path'] = '/var/www/mysite/private';Finish with TLS. Certbot handles issuance and renewal, and Linode’s DNS manager can hold the records if you delegate the domain to it.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo systemctl status certbot.timerCron, updates and day to day operations
Drupal’s built in cron runs on page requests, which means a quiet site never runs it and a busy site runs it at the worst possible moment. Disable that behavior in the admin interface and drive cron from the system instead.
sudo crontab -u www-data -e
# add this line
*/15 * * * * /var/www/mysite/vendor/bin/drush --root=/var/www/mysite cronUpdates go through Composer, never through uploading files. The pattern is the same every time: update, run database updates, rebuild caches.
cd /var/www/mysite
sudo -u www-data composer update drupal/core-* --with-all-dependencies
sudo -u www-data ./vendor/bin/drush updatedb -y
sudo -u www-data ./vendor/bin/drush cache:rebuild
sudo -u www-data ./vendor/bin/drush statusBack up the database on a schedule too, with drush sql:dump piped to a compressed file and copied off the instance. If you have set up other CMS platforms before, the operational rhythm will look familiar from our guides to creating a Joomla website and launching MODX on Linode, and if you would rather start on shared hosting first, see launching Drupal on HostGator.
Tuning the instance for real traffic
A default install will feel sluggish under load because three settings are left conservative. Raise the OPcache memory allocation so all of Drupal’s PHP fits in the cache, since Drupal is a large codebase and the default allocation evicts constantly. Set opcache.memory_consumption to at least 192 and opcache.max_accelerated_files to 20000 in the FPM php.ini, then reload PHP FPM.
Second, cap PHP FPM workers to what your memory can actually support. Each worker on a Drupal site tends to sit somewhere in the tens of megabytes, so on a 2 GB instance with a database on the same box, a pm.max_children value in the low teens is realistic. Setting it higher does not make the site faster, it makes it swap and then fall over.
Third, turn on Drupal’s own page cache and dynamic page cache modules for anonymous visitors, and set a sensible browser cache lifetime under Configuration then Performance. On a content site that alone removes most of the PHP work. When traffic grows past what one instance handles, add Redis as the cache backend and put a CDN in front, in that order, before you reach for a bigger plan.
Troubleshooting Drupal on Linode
White screen with a 500 error after install. Check /var/log/nginx/error.log and the PHP FPM log. A missing extension, usually gd or intl, is the most frequent cause. Install it and restart PHP FPM.
Composer is killed during install. Out of memory on a 1 GB instance. Add swap, or run with php -d memory_limit=-1 /usr/local/bin/composer. Dependency resolution needs far more memory than the running site does.
The site says the trusted host setting is not configured. Add the trusted_host_patterns array to settings.php exactly as shown, using escaped regular expressions rather than plain domain strings.
Images upload but image styles never render. The files directory is not writable by www-data, or the styles location block is missing from the Nginx configuration. Fix ownership first, then the server block.
Cron never runs. The crontab was added for the wrong user, or the Drush path is wrong. Test it by hand with sudo -u www-data /var/www/mysite/vendor/bin/drush --root=/var/www/mysite cron and read the output.
Frequently asked questions
What PHP version does Drupal 11 need?
Drupal 11 requires PHP 8.3 or newer, and later minor releases add support for PHP 8.5. Ubuntu 24.04 LTS ships PHP 8.3 in its default repositories, which is why it pairs well with a Linode instance running Drupal 11.
Should I install Drupal with Composer or a tarball?
Composer, without exception. It tracks core and contributed module versions in composer.json, makes security updates a single command, and is the method the project documents. Tarball installs leave you updating files by hand with no dependency resolution.
How big a Linode do I need for Drupal?
A 2 GB plan at $12 per month handles most small production sites including the database. The 1 GB Nanode works for development and staging if you add swap. Move to 4 GB when you have many authenticated users or a large media library.
Where should the document root point?
At the web subdirectory inside the Composer project, not the project root. Pointing it at the project root exposes composer.json, the vendor tree and configuration files to anyone who requests them.
Do I need Drush?
You do not strictly need it, but you want it. Drush turns installs, cache rebuilds, database updates, user management and cron into single commands, and it is what every deployment script and every support answer assumes you have available.
The bottom line
Drupal on Linode is a straightforward LEMP build with three details that matter more than the rest: install with Composer, point the document root at web, and drive cron from the system rather than from page requests. Get those right and the site behaves.
Start on the 2 GB plan, take a snapshot before every core update, and keep the whole project under version control including composer.lock. That combination gives you a Drupal 11 site you can update confidently and rebuild from scratch on a new instance in under an hour.
