Target outcome: Once you finish, your domain will load a WordPress website on a Vultr VPS running Ubuntu 24.04 LTS, Apache, MySQL, PHP, HTTPS and permalinks. This guide uses a manual installation method and does not use the One-Click WordPress application in Vultr Marketplace.
For beginners, One-Click WordPress is easier because Vultr preinstalls the image and part of the stack. However, you still need to check the versions, administrator account, firewall, updates, backups and security configuration. The main procedure below is suitable when you want to understand where each component is located and troubleshoot problems yourself.
The process usually takes about 45–90 minutes if you are unfamiliar with Linux, excluding the time required for DNS updates. You need a Vultr account, a domain whose DNS records you can edit, a computer with SSH and a separate database password. VPS prices vary by plan and region; check the current price in your Vultr account before creating the server.
This procedure uses Apache with Apache’s PHP module, not Nginx or PHP-FPM. WordPress lists its server and PHP, MySQL/MariaDB version requirements on the WordPress.org Requirementspage; check them again when deploying because the requirements may change.
1. Prepare the VPS, domain and SSH access
Create an Ubuntu VPS on Vultr
Sign in to Vultr, create a new server and select the following settings:
- Server type: Cloud Compute; a new or low-traffic website can start with a shared-CPU plan that fits your budget.
- Region: choose a location close to most of your visitors.
- Operating system: Ubuntu 24.04 LTS x64.
- Login method: SSH keys are preferred. If you use the root password, change it to a long, unique password that is not reused for another service.
- Hostname: choose an identifiable name, such as
web-01.
Vultr’s documentation describes the Cloud Compute creation process in Vultr Cloud Compute Provisioning. Once the server has been created, record the following:
- IPv4: replace the example
203.0.113.10in this guide with the VPS’s actual IP address. - Domain: for example
example.com. - SSH account: usually
rootfor the first login, or an account withsudoif your image has already created that account.
Do not expose MySQL directly to the Internet. WordPress and MySQL run on the same VPS in this guide, so the database only needs to listen locally.
Point the DNS records
In your domain registrar’s DNS management panel, create or edit the following records:
| Type | Name/Host | Value | Purpose |
|---|---|---|---|
| A | @ | VPS IPv4 address | Primary domain |
| A | www | VPS IPv4 address | Domain with the www prefix |
Delete or edit any old A records pointing to another server. If IPv6 is not configured on the VPS, delete any old AAAA records; otherwise, some browsers may connect to the wrong IPv6 address. DNS changes may not take effect immediately, but the domain must point to the correct address before you request a Let’s Encrypt certificate.
Check from your computer, not from the SSH session:
nslookup example.com
nslookup www.example.com
The result must contain the VPS IPv4 address. Replace example.com with your actual domain name.
Log in over SSH and update the server
Run this in the macOS/Linux Terminal or Windows PowerShell. Replace 203.0.113.10 with the actual IP address:
ssh root@203.0.113.10
If you are logging in with an SSH key:
ssh -i ~/.ssh/ten-khoa root@203.0.113.10
Replace ~/.ssh/ten-khoa with the path to the private key on your computer. Do not share the private key with anyone or paste it into this guide or a support ticket.
After you see the VPS command prompt, run this on the VPS:
apt update && apt upgrade -y
apt install -y curl unzip ca-certificates software-properties-common ufw
hostnamectl set-hostname web-01
Check the system:
lsb_release -a
free -h
df -h
If the system requests a reboot after a kernel update, perform it:
reboot
Wait approximately 30–60 seconds, then log in over SSH again. Keep one active SSH session open when changing the firewall; do not enable the firewall before allowing SSH access.
2. Install Apache, MySQL, PHP, and OPcache
Install the LAMP packages
Run this on the VPS:
apt install -y apache2 mysql-server php libapache2-mod-php php-mysql php-curl php-gd php-imagick php-mbstring php-xml php-zip php-intl php-bcmath php-cli php-opcache
Enable the services at startup and check their status:
systemctl enable --now apache2 mysql
systemctl is-active apache2
systemctl is-active mysql
php -v
mysql --version
php -m | grep -E 'curl|dom|fileinfo|gd|imagick|mbstring|mysqli|mysqlnd|openssl|xml|zip|Zend OPcache'
Expected result: the two is-active commands return active; php -v and mysql --version return the version; the module list includes the required extensions and Zend OPcache.
Ubuntu provides instructions for installing and checking MySQL in the Ubuntu Server Documentation: Install and configure a MySQL server.
Check that Apache processes PHP
Temporarily create a test file on the VPS:
printf '%sn' '<?php echo "PHP OK ".PHP_VERSION; ?>' > /var/www/html/check.php
curl http://127.0.0.1/check.php
The result must begin with PHP OK and include the PHP version. Delete the file immediately after testing because environment information should not be publicly exposed:
rm -f /var/www/html/check.php
If curl returns PHP source code instead of the result, Apache does not have the correct PHP handler. Check the libapache2-mod-phppackage, the Apache logs, and restart Apache after making corrections.
Adjust PHP limits and OPcache
On the VPS, determine the installed PHP version, then open php.ini for Apache:
PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
echo "$PHP_VERSION"
nano "/etc/php/$PHP_VERSION/apache2/php.ini"
In the nanoeditor, find and set the following values. If a line begins with a semicolon, remove it:
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120
max_input_time = 120
post_max_size should be greater than or equal to upload_max_filesize. Save with Ctrl+O, press Enter, and exit with Ctrl+X.
Check PHP’s OPcache configuration file:
ls "/etc/php/$PHP_VERSION/mods-available/opcache.ini"
nano "/etc/php/$PHP_VERSION/mods-available/opcache.ini"
Make sure the following basic lines are present; if they already exist, change only their values:
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
Restart Apache to apply the configuration:
systemctl restart apache2
php -i | grep -E 'memory_limit|upload_max_filesize|post_max_size|opcache.enable'
Do not set the limits too high on a low-memory VPS. If uploading a theme, plugin, or large image still fails, check memory usage with free -h and review the Apache logs before increasing the limits further.
3. Create a separate MySQL database and user
Do not use the MySQL account root in WordPress. On the VPS, open the MySQL client with system privileges:
mysql
When you see the prompt mysql>, paste the entire block below. Replace the sample password with a long, random string used only for this database:
CREATE DATABASE wp_site CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'THAY_BANG_MAT_KHAU_DAI_NGAU_NHIEN';
GRANT ALL PRIVILEGES ON wp_site.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Do not enclose the entire password in quotation marks in the SQL command. If the password contains special characters such as a single quotation mark, choose a different password that can be pasted safely, or create the account using a secrets-management tool.
Check from the VPS shell:
mysql -u wp_user -p -e "SHOW DATABASES;"
Enter the database password. The result should contain wp_site. The official WordPress database documentation is available at Creating Database for WordPress.
4. Download WordPress and Configure Apache
Download the official WordPress source code
Run these commands on the VPS. They back up the default Apache page, create the website directory, and download the package from WordPress.org:
mv /var/www/html /var/www/html.apache-default
mkdir -p /var/www/html
cd /tmp
curl -fLO https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
cp -a wordpress/. /var/www/html/
latest.tar.gz This is the stable package provided by WordPress.org at the time of download. Do not use a WordPress copy from an untrusted source.
Create the Virtual Host
The block below uses the example domain example.com. The safest approach is to create the file with a heredoc on the VPS; replace example.com in the filename, ServerName and ServerAlias with your actual domain before running it:
cp /etc/apache2/sites-available/000-default.conf /root/000-default.conf.backup
cat > /etc/apache2/sites-available/example.com.conf <<'APACHE'
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/html
<Directory /var/www/html>
AllowOverride All
Options FollowSymLinks
Require all granted
</Directory>
DirectoryIndex index.php index.html
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
APACHE
a2enmod rewrite
a2dissite 000-default.conf
a2ensite example.com.conf
apache2ctl configtest
systemctl reload apache2
To avoid errors caused by leading spaces, run the three site-enabling commands separately if necessary:
a2dissite 000-default.conf
a2ensite example.com.conf
apache2ctl configtest && systemctl reload apache2
The output of apache2ctl configtest must be Syntax OK. AllowOverride All allows WordPress to use .htaccess to handle permalinks. Do not reload Apache if the syntax check fails.
See the WordPress server configuration documentation at WordPress Server Configuration. Although the page title refers to Nginx, the WordPress documentation also describes general server-configuration requirements; in this guide, the actual web server is Apache.
Set File Permissions
WordPress needs the web server to read the source code and write to certain required directories. Run:
chown -R www-data:www-data /var/www/html
find /var/www/html -type d -exec chmod 755 {} ;
find /var/www/html -type f -exec chmod 644 {} ;
chmod 640 /var/www/html/wp-config.php 2>/dev/null || true
wp-config.php does not yet exist before the installer runs, so the final command may make no changes. After WordPress creates this file, run the command again:
chmod 640 /var/www/html/wp-config.php
chown www-data:www-data /var/www/html/wp-config.php
Do not use chmod -R 777. Overly broad write permissions increase the risk if a plugin or process is exploited.
5. Configure the Firewall and Run the WordPress Installer
Open the Required Network Ports
On the VPS, allow SSH before enabling UFW:
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status verbose
The output must allow TCP ports 22, 80, and 443. If you use a Vultr Firewall Group, allow these three ports there as well. Manage the rules at Vultr Firewall Rules. Do not expose port 3306 to the Internet if MySQL serves only WordPress on the same VPS.
If you are using a firewall other than UFW, do not enable UFW as well until you understand the existing rules; two firewall layers can make troubleshooting more difficult.
Run the Installer
Open http://example.com in a browser, replacing it with your actual domain. Select a language, then enter:
- Site Title: the name displayed for the website.
- Username: do not use
admin; choose a name that is not easy to guess. - Password: use the password generated by WordPress or a long, unique password.
- Email: an address you can still access.
If the form asks for database details, enter them exactly as follows:
- Database Name:
wp_site - Username:
wp_user - Password: the password created in MySQL
- Database Host:
localhost - Table Prefix: you may use a different prefix
wp_, such assite7_, if you understand why it needs to be changed
At this stage, the WordPress Address and Site Address may still use HTTP. Do not edit the database directly to switch to HTTPS; use Certbot in the next step, then verify the URLs under Settings > General.
6. Enable HTTPS with Let’s Encrypt
Proceed only when:
example.comandwww.example.comboth point to the VPS’s correct IPv4 address;- port 80 is open in both UFW and the Vultr Firewall, if enabled;
- the website is accessible over HTTP from an external network.
On the VPS, install Certbot and the Apache plugin:
apt install -y certbot python3-certbot-apache
certbot --apache -d example.com -d www.example.com
Replace the two example domains with your actual domain names. When prompted, enter your email address, agree to the terms, and choose to redirect HTTP to HTTPS if you want all traffic to use HTTPS.
Check the certificate:
certbot certificates
systemctl status certbot.timer --no-pager
certbot renew --dry-run
The expected result of certbot renew --dry-run is a completed dry run with no errors. If certificate issuance fails, check DNS, port 80, the domain www and any proxy/CDN in use; do not delete WordPress data to troubleshoot an SSL problem.
You can refer to the Let’s Encrypt automatic-renewal guide. WordPress provides HTTPS documentation at WordPress HTTPS Documentation.
After Certbot completes the redirect, open https://example.com/wp-admin, go to Settings > General and confirm that both the WordPress Address and the Site Address begin with https://.
7. Verify the Website and Troubleshoot Common Issues
Check the Services and HTTP
Run these commands on the VPS:
apache2ctl configtest
systemctl is-active apache2
systemctl is-active mysql
curl -I https://example.com
curl -I https://example.com/wp-admin/
Both service commands should return active. The curl commands may return 200, 301 or 302 depending on the redirect and login state; codes 500 or 503 require checking the logs.
Check Permalinks and File Uploads
In WordPress, go to Settings > Permalinks, select Post name, click Save Changes, then open a post in a private window. Try uploading an image and installing a plugin from a legitimate source.
If a post returns a 404 error:
a2enmod rewrite
apache2ctl configtest
systemctl reload apache2
ls -la /var/www/html/.htaccess
If file uploads fail, check upload_max_filesize, post_max_size, directory permissions, and the module list:
PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
php -m
php -i | grep -E 'memory_limit|upload_max_filesize|post_max_size|max_execution_time'
ls -ld /var/www/html /var/www/html/wp-content
There is no need to leave a PHP test file publicly accessible after verification. For image optimization, see how to reduce server load when uploading AVIF and HEIC images. For interface testing, see how to check responsive breakpoints in WordPress.
Symptom-Based Diagnosis
| Symptom | Diagnosis | Safe remediation |
|---|---|---|
| Unable to connect via SSH | Check the IP address, SSH key, port 22, VPS status and Vultr Firewall. | Keep a backup SSH session open; do not change the port or enable a new policy until you have an alternative access path. |
| 403 Forbidden | Check DocumentRoot, directory permissions and Require all granted. | Run apache2ctl configtest and then review the error log; never grant 777 permissions. |
| 500, 502 or a blank page | Check the status of Apache, the PHP modules and the Apache error log. | Undo the latest change in php.ini or the Virtual Host, then reload after configtest reports a valid configuration. |
| Unable to connect to the database | Compare the database name, username, password and host in wp-config.php. | Try mysql -u wp_user -p -e "SHOW DATABASES;"; do not put the password in a URL or log. |
| Unable to issue an SSL certificate | Check the A/AAAA records, DNS, port 80, the www domain and the proxy. | Fix the DNS or firewall, then run Certbot again; do not delete the WordPress directory. |
| Permalinks return 404 | Check the rewritemodule, AllowOverride All and the .htaccessfile. | Enable rewrite, check the configuration syntax and reload Apache. |
| Plugin/theme upload fails | Check the PHP extensions, the permissions for www-data and the file-upload limits. | Review the logs before increasing any limits; use only plugins and themes from legitimate, trusted sources. |
View the Virtual Host logs on the VPS:
tail -n 50 /var/log/apache2/example.com-error.log
tail -n 50 /var/log/apache2/example.com-access.log
Replace example.com with the actual domain if you configured a different log name. For safer plugin management, see the 24-hour plugin-check procedure before updating. To add a layer of defense against unfamiliar bots and crawlers, see how to secure WordPress against AI bots and crawlers.
8. Backups and rollback
Create a backup before updating
Back up both the database and the files. Run this on the VPS; the command will prompt for the MySQL password:
mkdir -p /root/backup-wordpress
mysqldump --single-transaction -u wp_user -p wp_site | gzip > "/root/backup-wordpress/wp_site-$(date +%F).sql.gz"
tar -czf "/root/backup-wordpress/wp-files-$(date +%F).tar.gz" /var/www/html
ls -lh /root/backup-wordpress
A sole backup stored on the same VPS will not protect you if the VPS fails or is deleted. Download the backup files to your personal computer or store them elsewhere, control access permissions, and periodically test extraction and restoration.
Restore after an update causes an error
Before restoring, create another backup of the current state. For files, extract the backup into a temporary directory for inspection first:
mkdir -p /root/restore-check
tar -xzf /root/backup-wordpress/wp-files-YYYY-MM-DD.tar.gz -C /root/restore-check
ls -la /root/restore-check/var/www/html
Replace YYYY-MM-DD with the actual backup date. Replace the website directory only after confirming that the backup is correct and that you have a copy of the current state:
mv /var/www/html /var/www/html.failed-$(date +%F-%H%M)
mkdir -p /var/www/html
tar -xzf /root/backup-wordpress/wp-files-YYYY-MM-DD.tar.gz -C /
chown -R www-data:www-data /var/www/html
find /var/www/html -type d -exec chmod 755 {} ;
find /var/www/html -type f -exec chmod 644 {} ;
apache2ctl configtest && systemctl reload apache2
Replace the backup filename as appropriate. For the database, do not immediately overwrite the active database unless you are certain; create a temporary database for testing or put the website into maintenance mode, then restore the correct backup according to your plan. If the error appeared only after editing the Virtual Host, restore the saved configuration file at /root/000-default.conf.backup, run apache2ctl configtest, and only then reload.
9. Completion checklist
- The Ubuntu 24.04 LTS VPS has been created and its IPv4 address has been recorded.
- The A record for the primary domain and
wwwpoint to the correct VPS; no outdated AAAA record points to the wrong address. - SSH access works; the administrative session remains open after configuring the firewall.
- Apache and MySQL both return
active. - PHP has the required modules, and OPcache is enabled.
memory_limit,upload_max_filesizeandpost_max_sizehave been checked after reloading Apache.- The WordPress database uses a dedicated user account rather than the MySQL root account.
- The virtual host returns
Syntax OKand the website loads successfully. - The firewall allows only the required ports: 22, 80, and 443.
- The website opens over HTTPS, the certificate matches the domain name, and
certbot renew --dry-runthe setup completes successfully. - Log in
wp-admin, open the permalink settings, and test a file upload; all should work. - You have a copy of the database and files stored outside the VPS, and you know where the backups are located so you can roll back if necessary.
If your only goal is to get a website running quickly and you do not want to manage Apache, MySQL, PHP, the firewall, and Certbot yet, One-Click WordPress in the Vultr Marketplace is the easier option. Even with a prebuilt image, you should still check the versions, accounts, updates, HTTPS, firewall, and backups. Manual installation is a better fit when you want control over every component and can diagnose problems yourself.

