Drupal has a reputation for being complicated, and it's not entirely undeserved — it's a more powerful and more opinionated CMS than most. But for certain use cases, especially structured content with complex relationships, it's genuinely the right tool. Universities, government agencies, and large content platforms use it because of that flexibility, not despite the learning curve.
I've deployed Drupal for a few client projects where the content structure was too complex for WordPress. The deployment process is predictable once you know the quirks — primarily around file permissions and the trusted host patterns configuration.
This guide deploys Drupal 10 on Ubuntu 22.04 with Nginx, PHP 8.2, and MySQL, including the permission setup and configuration that most guides gloss over.
I run this on Tencent Cloud Lighthouse with the 2 vCPU / 4 GB RAM plan. Drupal with Nginx and PHP-FPM is efficient and handles a production site comfortably at this spec. The snapshot feature is worth using before Drupal module updates — Drupal's update process can occasionally break things, and having a full server snapshot means you can restore in under 5 minutes if a module update causes problems. Lighthouse's clean Ubuntu images also mean you can test a Drupal deployment on a fresh instance and know it's not inheriting any environment-specific issues.
- Key Takeaways
Drupal excels when you need:
For a personal blog or simple website, WordPress or Ghost is simpler. Choose Drupal when the content architecture is complex.
| Requirement | Notes |
|---|---|
| Cloud server | Tencent Cloud Lighthouse Ubuntu 22.04 |
| PHP 8.2 | Drupal 10 minimum requirement |
| MySQL / MariaDB | We'll use MySQL |
| Composer | PHP dependency manager |
ssh ubuntu@YOUR_SERVER_IP
sudo apt update && sudo apt upgrade -y
# PHP repository for latest versions
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
# Install Nginx + PHP 8.2 + required extensions for Drupal
sudo apt install -y nginx php8.2-fpm php8.2-mysql php8.2-xml \
php8.2-gd php8.2-curl php8.2-mbstring php8.2-zip php8.2-intl \
php8.2-opcache php8.2-apcu
# Install MySQL
sudo apt install -y mysql-server
sudo systemctl start mysql && sudo systemctl enable mysql
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable
Configure PHP for Drupal (edit /etc/php/8.2/fpm/php.ini):
sudo nano /etc/php/8.2/fpm/php.ini
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
date.timezone = UTC
sudo systemctl restart php8.2-fpm
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer
composer --version
sudo mysql_secure_installation # If not already done
sudo mysql
CREATE DATABASE drupal_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'drupal_user'@'localhost' IDENTIFIED BY 'strong_password_here';
GRANT ALL PRIVILEGES ON drupal_db.* TO 'drupal_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
cd /var/www
# Create Drupal project using Composer (recommended method)
sudo composer create-project drupal/recommended-project drupal
# Set ownership
sudo chown -R ubuntu:www-data /var/www/drupal
sudo chmod -R 755 /var/www/drupal
sudo chmod -R 775 /var/www/drupal/web/sites/default/files
The Composer method downloads Drupal core plus recommended modules and sets up a proper project structure.
sudo nano /etc/nginx/sites-available/drupal
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
root /var/www/drupal/web;
index index.php index.html;
access_log /var/log/nginx/drupal_access.log;
error_log /var/log/nginx/drupal_error.log;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Block hidden files
location ~ \..*/.*\.php$ { return 403; }
location ~ ^/sites/.*/private/ { return 403; }
location ~ ^/sites/[^/]+/files/.*\.php$ { deny all; }
location ~ (^|/)\. { return 403; }
# Allow "Well-Known" URIs for Let's Encrypt
location ~* ^/.well-known/ { allow all; }
# Drupal clean URLs
location / {
try_files $uri /index.php?$query_string;
}
# PHP via PHP-FPM
location ~ '\.php$|^/update.php' {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $request_filename;
include fastcgi_params;
}
# Static file caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
try_files $uri @rewrite;
expires max;
log_not_found off;
}
# Drupal image styles
location ~ ^/sites/.*/files/styles/ {
try_files $uri @rewrite;
}
location @rewrite {
rewrite ^ /index.php;
}
}
sudo ln -s /etc/nginx/sites-available/drupal /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Visit http://yourdomain.com in your browser. The Drupal installation wizard appears:
drupal_dbdrupal_userlocalhostInstallation takes 1-2 minutes.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
After HTTPS is configured, set the trusted host patterns in Drupal:
sudo nano /var/www/drupal/web/sites/default/settings.php
Add at the end:
$settings['trusted_host_patterns'] = [
'^yourdomain\.com$',
'^www\.yourdomain\.com$',
];
Drupal recommends read-only settings file after installation:
sudo chmod 444 /var/www/drupal/web/sites/default/settings.php
sudo chmod 555 /var/www/drupal/web/sites/default
cd /var/www/drupal
composer require drush/drush
# Add to PATH
echo 'export PATH="$PATH:/var/www/drupal/vendor/bin"' >> ~/.bashrc
source ~/.bashrc
drush status
# Run Drupal cron every hour
(crontab -l; echo "0 * * * * cd /var/www/drupal && vendor/bin/drush core:cron >> /var/log/drupal-cron.log 2>&1") | crontab -
sudo apt install -y redis-server php8.2-redis
composer require drupal/redis
# Enable in Drupal admin: Extend → Redis → Install
# Configure in settings.php:
$settings['redis.connection']['interface'] = 'PhpRedis';
$settings['redis.connection']['host'] = '127.0.0.1';
$settings['cache']['default'] = 'cache.backend.redis';
The most common Drupal issue: incorrect file permissions causing errors when uploading images, installing modules, or writing config.
Drupal's web server (www-data) needs write access to the files directory and settings:
# During setup and development
sudo chown -R ubuntu:www-data /var/www/drupal/web/sites
sudo chmod -R 775 /var/www/drupal/web/sites/default/files
sudo chmod 664 /var/www/drupal/web/sites/default/settings.php
# After installation (tighten for security)
sudo chmod 444 /var/www/drupal/web/sites/default/settings.php
sudo chmod 555 /var/www/drupal/web/sites/default
If modules or configuration can't be written during admin operations:
# Temporarily re-enable write access
sudo chmod 644 /var/www/drupal/web/sites/default/settings.php
# Do your admin work
# Then re-tighten
sudo chmod 444 /var/www/drupal/web/sites/default/settings.php
# Status and info
drush status
drush pm-list --status=enabled # List enabled modules
# Cache
drush cache:rebuild # Rebuild all caches (drush cr)
# Database
drush sql:dump > backup.sql # Backup database
drush sql:cli < backup.sql # Import database
# Updates
drush pm:update # Check for module updates
drush pm:update MODULE_NAME # Update specific module
drush updatedb # Run pending database updates
# Configuration
drush config:export # Export config to files
drush config:import # Import config from files
# User management
drush user:create USERNAME --password=PASS --mail=EMAIL
drush user:role:add administrator USERNAME
# Cron
drush core:cron # Run cron manually
| Issue | Likely Cause | Fix |
|---|---|---|
| Connection refused | Service not running or wrong port | Check systemctl status SERVICE and verify firewall rules |
| Permission denied | Wrong file ownership or permissions | Check file ownership with ls -la and use chown/chmod to fix |
| 502 Bad Gateway | Backend service not running | Restart the backend service; check logs with journalctl -u SERVICE |
| SSL certificate error | Certificate expired or domain mismatch | Run sudo certbot renew and verify domain DNS points to server IP |
| Service not starting | Config error or missing dependency | Check logs with journalctl -u SERVICE -n 50 for specific error |
| Out of disk space | Logs or data accumulation | Run df -h to identify usage; clean logs or attach CBS storage |
| High memory usage | Too many processes or memory leak | Check with htop; consider upgrading instance plan if consistently high |
| Firewall blocking traffic | Port not open in UFW or Lighthouse console | Open port in Lighthouse console firewall AND sudo ufw allow PORT |
Can I migrate an existing Drupal site to a cloud server?
Yes. Export your content and database from the current host, import them on the new server, then update your DNS to point to the new IP. Update any hardcoded URLs in the database or configuration files.
How do I keep Drupal updated securely?
Enable automatic minor updates where available, and manually update major versions after testing on a staging environment. Take a Lighthouse snapshot before any significant update.
Do I need a CDN for my Drupal site?
For international audiences, yes. A CDN like EdgeOne caches static assets at edge nodes worldwide, improving load times for visitors far from your server region.
What causes Drupal to be slow on a VPS?
Usually: missing caching plugin/configuration, unoptimized images, too many plugins, or insufficient server RAM. Start with a caching layer (Redis or built-in cache) and image optimization.
Deploy Drupal today:
👉 Tencent Cloud Lighthouse — PHP-ready Ubuntu VPS
👉 View current pricing and promotions
👉 Explore all active deals and offers