Setting up WordPress manually can be time-consuming and error-prone, but with automation, it becomes a seamless task. This script automates the entire process of installing WordPress on an Ubuntu server using NGINX as a reverse proxy, PHP-FPM for efficient PHP processing, and MySQL for database management. Let’s break down the script and understand how it works.
Why Choose NGINX and PHP-FPM for WordPress?
NGINX is known for its high performance and low resource usage compared to traditional servers like Apache. Paired with PHP-FPM (FastCGI Process Manager), it ensures faster PHP processing, making your WordPress site load faster and handle more visitors with ease.
Learn more about NGINX and PHP-FPM from their official documentation.
Prerequisites
Before using the script, ensure your server meets these requirements:
- A fresh Ubuntu (20.04 or higher) instance.
- Root or sudo access.
- A registered domain (optional but recommended).
- Basic understanding of SSH and server management.
Install Required Packages
- NGINX
- PHP-FPM
- MySQL/MariaDB
- PHP extensions (like php-mysql, php-xml, php-gd, etc.)
Configure Database
A new MySQL database and user are created automatically, with secure random passwords.
Download and Configure WordPress
The script downloads the latest WordPress package, extracts it, and sets up proper file permissions for security.
Configure NGINX
The script generates an optimized NGINX server block configuration, connecting your domain to the WordPress installation.
Step 1: Define Environment Variables
The script begins by defining essential variables such as database credentials, site details, and PHP settings. These values can be hardcoded or loaded from a .env
file.
DB_NAME=wp
DB_USER=wp
DB_PASSWORD=12345687
DB_HOST=localhost
WP_ADMIN_USER=user
WP_ADMIN_PASSWORD=1234567
WP_ADMIN_EMAIL=abc@gmail.com
WP_SITE_URL=https://abc.co.in
WP_SITE_TITLE="devops"
PHP_VERSION=8.2
UPLOAD_MAX_SIZE=128M
POST_MAX_SIZE=128M
MAX_EXECUTION_TIME=600
FOLDER_NAME=${WP_SITE_TITLE}
NGINX_CONF="/etc/nginx/nginx.conf"
PHP_MEMORY_LIMIT=128M
MYSQL_ROOT_PASSWORD="abc#123"
DOMAIN="abc.co.in"
EMAIL="abc@gmail.com"
SITE_CONF_PATH="/etc/nginx/conf.d/devops.conf"
These variables control database settings, WordPress site configuration, and PHP/Nginx settings.
Update and Install Required Packages
The script ensures that all necessary system packages are up to date and installs MySQL, NGINX, and PHP-FPM along with essential PHP extensions.
Step 2: Update and Install Required Packages
The script ensures that all necessary system packages are up to date and installs MySQL, NGINX, and PHP-FPM along with essential PHP extensions.
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx-full mysql-server software-properties-common unzip wget build-essential libxml2-dev pkg-config curl
Additionally, PHP 8.2 is installed with all necessary modules for WordPress to function optimally.
Step 3: Configure MySQL
The script installs MySQL and sets up the root user with password authentication. It then creates a dedicated database and user for WordPress.
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME};"
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASSWORD}';"
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%';"
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "FLUSH PRIVILEGES;"
Step 4: Configure PHP-FPM
Since NGINX relies on PHP-FPM for processing PHP files, the script updates PHP-FPM settings, adjusts permissions, and restarts the service.
PHP_INI_FILE="/etc/php/${PHP_VERSION}/fpm/php.ini"
sudo sed -i "s/upload_max_filesize = .*/upload_max_filesize = ${UPLOAD_MAX_SIZE}/" $PHP_INI_FILE
sudo sed -i "s/post_max_size = .*/post_max_size = ${POST_MAX_SIZE}/" $PHP_INI_FILE
sudo sed -i "s/max_execution_time = .*/max_execution_time = ${MAX_EXECUTION_TIME}/" $PHP_INI_FILE
sudo sed -i "s/memory_limit = .*/memory_limit = ${PHP_MEMORY_LIMIT}/" $PHP_INI_FILE
sudo systemctl restart php${PHP_VERSION}-fpm
Step 5: Configure NGINX
To serve WordPress efficiently, NGINX is set up with the correct user permissions and configuration files.
sudo cp $NGINX_CONF ${NGINX_CONF}.bak
sudo sed -i "s/^user .*/user $USER;/" $NGINX_CONF
if sudo nginx -t; then
sudo systemctl restart nginx
echo "✅ NGINX user updated to: $USER"
else
echo "❌ NGINX configuration test failed. Rolling back..."
sudo mv ${NGINX_CONF}.bak $NGINX_CONF
fi
This ensures NGINX runs as the correct user and avoids permission issues.
Step 6: Download and Install WordPress
The script then downloads and extracts the latest WordPress files into the specified directory. If WordPress is already installed, it skips this step.
if [ ! -f /var/www/code/"${FOLDER_NAME}"/wp-config.php ]; then
echo "Downloading WordPress..."
cd /var/www/code/"${FOLDER_NAME}"
sudo wget https://wordpress.org/latest.tar.gz
sudo tar -xzvf latest.tar.gz --strip-components=1
sudo rm latest.tar.gz
sudo mv wp-config-sample.php wp-config.php
fi
Step 7: Set File Permissions
Ownership and file permissions are updated to ensure WordPress can function correctly without security risks.
sudo chown -R $USER:$USER /var/www/code/"${FOLDER_NAME}"
sudo find /var/www/code/"${FOLDER_NAME}" -type d -exec chmod 755 {} \;
sudo find /var/www/code/"${FOLDER_NAME}" -type f -exec chmod 644 {} \;
Step 8: Install WP-CLI and Finalize WordPress Setup
To automate WordPress installation, WP-CLI (WordPress Command Line Interface) is installed and used to configure the site with admin credentials.
if ! command -v wp &> /dev/null; then
echo "Installing WP-CLI..."
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
sudo chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
fi
Then, the WordPress installation is completed with the provided site details:
cd /var/www/code/"${FOLDER_NAME}"
sudo -u $USER wp core install --url="${WP_SITE_URL}" --title="${WP_SITE_TITLE}" --admin_user="${WP_ADMIN_USER}" --admin_password="${WP_ADMIN_PASSWORD}" --admin_email="${WP_ADMIN_EMAIL}"
Step 9: Configure SSL with Let’s Encrypt
To secure the website, the script installs Certbot and generates an SSL certificate for the domain.
# Step 1: Create Nginx HTTP Config (Without SSL First)
cat <<EOL | sudo tee ${SITE_CONF_PATH}
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
root /var/www/code/${FOLDER_NAME};
index index.php index.html index.htm;
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
include fastcgi_params;
}
}
EOL
# Step 2: Reload Nginx Without SSL (Prevents Certbot Errors)
sudo systemctl restart nginx
# Step 3: Install Certbot
echo "Installing Certbot and NGINX plugin..."
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
# Step 5: Obtain SSL Certificate from Let's Encrypt
# sudo certbot certonly --nginx -d ${DOMAIN} -d www.${DOMAIN} --non-interactive --agree-tos --email ${EMAIL}
sudo certbot --nginx -d ${DOMAIN} -d www.${DOMAIN} --non-interactive --agree-tos --email ${EMAIL}
# Step 6: Update Nginx Config to Use SSL
cat <<EOL | sudo tee ${SITE_CONF_PATH}
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
return 301 https://\$host\$request_uri;
}
server {
listen 443 ssl;
server_name ${DOMAIN} www.${DOMAIN};
root /var/www/code/${FOLDER_NAME};
index index.php index.html index.htm;
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# ????️ Security Headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
# ???? Prevent access to hidden files (like .htaccess, .env)
location ~ /\. {
deny all;
}
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
include fastcgi_params;
}
}
EOL
# Step 7: Restart Nginx With SSL Enabled
sudo systemctl restart nginx
# Step 8: Set Up Auto-Renewal for SSL
echo "Setting up automatic renewal..."
sudo certbot renew --dry-run
echo "SSL installation complete for $DOMAIN. Visit https://$DOMAIN to verify."
Once the SSL certificate is generated, the NGINX configuration is updated to force HTTPS.
Conclusion
This script automates the deployment of a fully functional and secure WordPress site in a single click. By setting up MySQL, PHP-FPM, NGINX, WordPress, and SSL, it ensures that the website is production-ready with minimal manual intervention. Whether you’re setting up a new website or automating deployments, this script provides a solid foundation for WordPress hosting on Ubuntu servers.
Here is the complete script:
#!/bin/bash
## install wordpress in ubuntu ##
### https://www.linode.com/docs/guides/securing-mysql/ doc for install secure install mysql
# Load environment variables from .env file
set -a
#source .env
set +a
##### make the .env file if you want to use value from .env file #####
DB_NAME=wp
DB_USER=wp
DB_PASSWORD=12345687
DB_HOST=localhost
WP_ADMIN_USER=user
WP_ADMIN_PASSWORD=1234567
WP_ADMIN_EMAIL=abc@gmail.com
WP_SITE_URL=https://abc.co.in
WP_SITE_TITLE="devops"
PHP_VERSION=8.2
UPLOAD_MAX_SIZE=128M
POST_MAX_SIZE=128M
MAX_EXECUTION_TIME=600
FOLDER_NAME=${WP_SITE_TITLE}
NGINX_CONF="/etc/nginx/nginx.conf"
PHP_MEMORY_LIMIT=128M
MYSQL_ROOT_PASSWORD="abc#123"
DOMAIN="abc.co.in"
EMAIL="abc@gmail.com"
SITE_CONF_PATH="/etc/nginx/conf.d/devops.conf"
# Update package information
echo "Updating package information..."
if sudo apt update -y; then
echo "Package information updated successfully."
else
echo "Failed to update package information."
exit 1
fi
# Install MySQL server
echo "Installing MySQL server..."
if sudo apt install mysql-server -y; then
echo "MySQL server installed successfully."
else
echo "Failed to install MySQL server."
exit 1
fi
# Enable and start MySQL service
echo "Starting and enabling MySQL service..."
if sudo systemctl start mysql && sudo systemctl enable mysql; then
echo "MySQL service started and enabled."
else
echo "Failed to start MySQL service."
exit 1
fi
# Set up root user authentication method and password
echo "Configuring MySQL root user authentication..."
sudo mysql -u root <<EOF
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '$MYSQL_ROOT_PASSWORD';
FLUSH PRIVILEGES;
EOF
if [ $? -eq 0 ]; then
echo "MySQL root user configured with password authentication successfully."
else
echo "Failed to configure MySQL root user."
exit 1
fi
# Update packages and install necessary software
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx-full mysql-server software-properties-common unzip wget build-essential libxml2-dev pkg-config curl
# Download and install PHP
echo "Downloading PHP version..."
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install -y php${PHP_VERSION}-fpm php${PHP_VERSION}-common php${PHP_VERSION}-mysql \
php${PHP_VERSION}-xml php${PHP_VERSION}-intl php${PHP_VERSION}-curl php${PHP_VERSION}-gd \
php${PHP_VERSION}-imagick php${PHP_VERSION}-cli php${PHP_VERSION}-dev php${PHP_VERSION}-imap \
php${PHP_VERSION}-mbstring php${PHP_VERSION}-opcache php${PHP_VERSION}-redis \
php${PHP_VERSION}-soap php${PHP_VERSION}-zip
# Variables for user and group
USER_NAME=${USER}
GROUP_NAME=${USER}
# Path to the configuration file
CONFIG_FILE="/etc/php/${PHP_VERSION}/fpm/pool.d/www.conf"
# Use sed to replace the lines in the configuration file with sudo
sudo sed -i.bak -e "s/^user = .*/user = $USER_NAME/" \
-e "s/^group = .*/group = $GROUP_NAME/" \
-e "s/^listen.owner = .*/listen.owner = $USER_NAME/" \
-e "s/^listen.group = .*/listen.group = $GROUP_NAME/" \
"$CONFIG_FILE"
sudo chown $USER:$USER /var/run/php/php8.1-fpm.sock
# Start and enable PHP-FPM
sudo systemctl start php${PHP_VERSION}-fpm
sudo systemctl enable php${PHP_VERSION}-fpm
# Change nginx user
# Backup the original file before making changes
sudo cp $NGINX_CONF ${NGINX_CONF}.bak
# Update the user directive using the $USER variable
sudo sed -i "s/^user .*/user $USER;/" $NGINX_CONF
# Restart NGINX if the configuration is valid
if sudo nginx -t; then
sudo systemctl restart nginx
echo "✅ NGINX user updated to: $USER"
else
echo "❌ NGINX configuration test failed. Rolling back..."
sudo mv ${NGINX_CONF}.bak $NGINX_CONF
fi
# Create MySQL database and user if they do not exist
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME};"
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASSWORD}';"
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%';"
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "FLUSH PRIVILEGES;"
# Update php.ini settings
PHP_INI_FILE="/etc/php/${PHP_VERSION}/fpm/php.ini"
sudo sed -i "s/upload_max_filesize = .*/upload_max_filesize = ${UPLOAD_MAX_SIZE}/" $PHP_INI_FILE
sudo sed -i "s/post_max_size = .*/post_max_size = ${POST_MAX_SIZE}/" $PHP_INI_FILE
sudo sed -i "s/max_execution_time = .*/max_execution_time = ${MAX_EXECUTION_TIME}/" $PHP_INI_FILE
sudo sed -i "s/memory_limit = .*/memory_limit = ${PHP_MEMORY_LIMIT}/" $PHP_INI_FILE
# Restart PHP-FPM
sudo systemctl restart php${PHP_VERSION}-fpm
# sudo systemctl start mysql
# sudo systemctl enable mysql
# Ensure the directory for the WordPress site exists
sudo mkdir -p /var/www/code/"${FOLDER_NAME}"
# Download WordPress if not already downloaded
if [ ! -f /var/www/code/"${FOLDER_NAME}"/wp-config.php ]; then
echo "Downloading WordPress to /var/www/code/${FOLDER_NAME}..."
cd /var/www/code/"${FOLDER_NAME}"
sudo wget https://wordpress.org/latest.tar.gz
echo "Extracting WordPress..."
sudo tar -xzvf latest.tar.gz --strip-components=1
# Clean up the tar file after extraction
sudo rm latest.tar.gz
sudo mv wp-config-sample.php wp-config.php
else
echo "WordPress is already installed in /var/www/code/${FOLDER_NAME}."
fi
# Set ownership and permissions
sudo chown -R $USER:$USER /var/www/code/"${FOLDER_NAME}"
sudo find /var/www/code/"${FOLDER_NAME}" -type d -exec chmod 755 {} \;
sudo find /var/www/code/"${FOLDER_NAME}" -type f -exec chmod 644 {} \;
# Replace placeholders in wp-config.php
echo "Updating wp-config.php with database credentials..."
# Ensure DB_NAME, DB_USER, DB_PASSWORD, and DB_HOST are updated regardless of their existing values
sed -i "s/define( 'DB_NAME', '[^']*' )/define( 'DB_NAME', '${DB_NAME}' )/" /var/www/code/"${FOLDER_NAME}"/wp-config.php
sed -i "s/define( 'DB_USER', '[^']*' )/define( 'DB_USER', '${DB_USER}' )/" /var/www/code/"${FOLDER_NAME}"/wp-config.php
sed -i "s/define( 'DB_PASSWORD', '[^']*' )/define( 'DB_PASSWORD', '${DB_PASSWORD}' )/" /var/www/code/"${FOLDER_NAME}"/wp-config.php
sed -i "s/define( 'DB_HOST', '[^']*' )/define( 'DB_HOST', '${DB_HOST}' )/" /var/www/code/"${FOLDER_NAME}"/wp-config.php
echo "wp-config.php updated with database credentials."
# Install wp-cli if not already installed
if ! command -v wp &> /dev/null; then
echo "Installing WP-CLI..."
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
sudo chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
fi
# WordPress installation
cd /var/www/code/"${FOLDER_NAME}"
sudo -u $USER wp core install --url="${WP_SITE_URL}" --title="${WP_SITE_TITLE}" --admin_user="${WP_ADMIN_USER}" --admin_password="${WP_ADMIN_PASSWORD}" --admin_email="${WP_ADMIN_EMAIL}"
# Done
echo "WordPress installed successfully at ${WP_SITE_URL}"
# Wait for MySQL to start
echo "Waiting for MySQL to be ready..."
until mysqladmin ping -h "localhost" --silent; do
echo "MySQL is not ready yet. Waiting..."
sleep 10
done
echo "MySQL is up and running."
# UPDATE wp_options SET option_value='https://debabrata.onestoptech.co.in' WHERE option_name IN ('siteurl', 'home');
#sudo systemctl restart mysql
# sudo systemctl reload nginx
# Step 1: Create Nginx HTTP Config (Without SSL First)
cat <<EOL | sudo tee ${SITE_CONF_PATH}
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
root /var/www/code/${FOLDER_NAME};
index index.php index.html index.htm;
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
include fastcgi_params;
}
}
EOL
# Step 2: Reload Nginx Without SSL (Prevents Certbot Errors)
sudo systemctl restart nginx
# Step 3: Install Certbot
echo "Installing Certbot and NGINX plugin..."
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
# Step 5: Obtain SSL Certificate from Let's Encrypt
# sudo certbot certonly --nginx -d ${DOMAIN} -d www.${DOMAIN} --non-interactive --agree-tos --email ${EMAIL}
sudo certbot --nginx -d ${DOMAIN} -d www.${DOMAIN} --non-interactive --agree-tos --email ${EMAIL}
# Step 6: Update Nginx Config to Use SSL
cat <<EOL | sudo tee ${SITE_CONF_PATH}
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
return 301 https://\$host\$request_uri;
}
server {
listen 443 ssl;
server_name ${DOMAIN} www.${DOMAIN};
root /var/www/code/${FOLDER_NAME};
index index.php index.html index.htm;
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# ????️ Security Headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
# ???? Prevent access to hidden files (like .htaccess, .env)
location ~ /\. {
deny all;
}
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
include fastcgi_params;
}
}
EOL
# Step 7: Restart Nginx With SSL Enabled
sudo systemctl restart nginx
# Step 8: Set Up Auto-Renewal for SSL
echo "Setting up automatic renewal..."
sudo certbot renew --dry-run
echo "SSL installation complete for $DOMAIN. Visit https://$DOMAIN to verify."