Hosting of WordPress on Azure Ubuntu VM with LEMP Stack

This guide provides a complete walkthrough for setting up a WordPress site on an Azure VM running Ubuntu. We will configure a LEMP (Linux, Nginx, MySQL, PHP) stack and set up WordPress.

Step 1: Create a VM in Azure

  1. Log in to your Azure Portal.

  2. Navigate to Virtual machines and click on + Create.

  3. Choose your subscription and resource group (create a new one if necessary).

  4. Enter a Virtual machine name.

  5. Choose a Region.

  6. Select an Image (e.g., Ubuntu 20.04 LTS).

  7. Choose a Size for your VM.

  8. Set up Administrator account with a username and SSH public key (or password).

  9. Under Inbound port rules, allow SSH (22) and HTTP (80).

  10. Click Next:Disk and then Next:Networking, check the box Delete public IP and NIC when VM is deleted

Click Review + create and then Create after validation passes.

And download the ssh key.

Your VM will be successfully created.

Step 2: Connect to Your VM

  • Once your VM is deployed, click Home and Virtual Machine and go to its Overview page and note the Public IP address.

  • Open the terminal where your ssh file .pem file is located and Connect to your VM via SSH: ssh -i your_username@your_vm_public_IP.

Step 3: Create a bash file script.sh nano script.sh and paste following configuration and save the file, ctrl s and ctrl x .

#!/bin/bash

# Exit on error
set -e

# Strict mode
set -euo pipefail
IFS=$'\n\t'

#---------------------------------------
# CONFIGURATION VARIABLES - MODIFY THESE
#---------------------------------------
# Domain Settings
HOSTNAME="aisolutions.com"                           # Your domain name

# Database Settings
WP_DB_NAME="aisolution_db"                       # WordPress database name
WP_DB_ADMIN_USER="siddhant"                      # WordPress database user
MYSQL_ROOT_PASSWORD="YourStr0ng!RootPass"        # MySQL root password
MYSQL_WP_ADMIN_USER_PASSWORD="YourStr0ng!WPPass" # WordPress database user password

# Installation Directories
WP_ROOT="/var/www/html/wordpress"
WP_BLOG_DIR="${WP_ROOT}/blog"
WP_SRC_DIR="${WP_ROOT}/src"

# PHP Version
PHP_VERSION="8.3"

# Nginx Settings
NGINX_CONFIG_DIR="/etc/nginx/sites-available"
CLIENT_MAX_BODY_SIZE="128M"                     # Maximum upload size

# WordPress Memory Limits
WP_MEMORY_LIMIT="256M"
WP_MAX_MEMORY_LIMIT="512M"

# WordPress Settings
WP_POST_REVISIONS=5                             # Number of post revisions to keep
WP_AUTO_UPDATE_CORE="minor"                     # WordPress auto-update setting
WP_DEBUG=false                                  # WordPress debug mode

# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'

#---------------------------------------
# Function Definitions
#---------------------------------------
print_status() {
    local color=$1
    local message=$2
    echo -e "${color}${message}${NC}"
}

# Check if script is run as root
if [[ "$EUID" -ne 0 ]]; then
    print_status "$RED" "Error: Please run as root with sudo."
    exit 1
fi

# Update system
print_status "$GREEN" "Updating system packages..."
apt-get update
apt-get -y upgrade
apt-get -y autoremove

# Install required software repositories
print_status "$GREEN" "Adding required repositories..."
apt-get install -y software-properties-common
add-apt-repository -y ppa:ondrej/php
apt-get update

# Install LEMP stack and WordPress dependencies
print_status "$GREEN" "Installing LEMP stack and WordPress dependencies..."
apt-get install -y \
    nginx \
    mysql-server \
    "php${PHP_VERSION}-fpm" \
    "php${PHP_VERSION}-cli" \
    "php${PHP_VERSION}-curl" \
    "php${PHP_VERSION}-mysql" \
    "php${PHP_VERSION}-gd" \
    "php${PHP_VERSION}-intl" \
    "php${PHP_VERSION}-mbstring" \
    "php${PHP_VERSION}-soap" \
    "php${PHP_VERSION}-xml" \
    "php${PHP_VERSION}-zip" \
    "php${PHP_VERSION}-imagick" \
    "php${PHP_VERSION}-redis" \
    unzip

# Configure Nginx
print_status "$GREEN" "Configuring Nginx..."
cat > "${NGINX_CONFIG_DIR}/${HOSTNAME}" <<EOF
server {
    listen 80;
    listen [::]:80;
    server_name ${HOSTNAME} www.${HOSTNAME};
    root ${WP_BLOG_DIR};
    index index.php index.html index.htm;

    # Increase body size limit for larger uploads
    client_max_body_size ${CLIENT_MAX_BODY_SIZE};

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # WordPress specific rules
    location / {
        try_files \$uri \$uri/ /index.php?\$args;
    }

    # PHP handling
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php${PHP_VERSION}-fpm.sock;
        fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_intercept_errors on;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 256k;
        fastcgi_busy_buffers_size 256k;
    }

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;

    # Deny access to sensitive files
    location ~ /\. {
        deny all;
    }

    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    location = /robots.txt {
        log_not_found off;
        access_log off;
        allow all;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires max;
        log_not_found off;
    }
}
EOF

rm -f /etc/nginx/sites-enabled/default
ln -sf "${NGINX_CONFIG_DIR}/${HOSTNAME}" /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

# Configure MySQL
print_status "$GREEN" "Configuring MySQL..."
mysql -u root <<EOF
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '${MYSQL_ROOT_PASSWORD}';
FLUSH PRIVILEGES;
EOF

# Secure MySQL installation
print_status "$GREEN" "Securing MySQL installation..."
mysql_secure_installation --user=root --password="${MYSQL_ROOT_PASSWORD}" --use-default

# Create WordPress database and user
print_status "$GREEN" "Creating WordPress database and user..."
mysql -u root --password="${MYSQL_ROOT_PASSWORD}" <<EOF
CREATE DATABASE IF NOT EXISTS ${WP_DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER '${WP_DB_ADMIN_USER}'@'localhost' IDENTIFIED BY '${MYSQL_WP_ADMIN_USER_PASSWORD}';
GRANT ALL PRIVILEGES ON ${WP_DB_NAME}.* TO '${WP_DB_ADMIN_USER}'@'localhost';
FLUSH PRIVILEGES;
EOF

# Install WordPress
print_status "$GREEN" "Installing WordPress..."
mkdir -p "${WP_SRC_DIR}" "${WP_BLOG_DIR}"
cd "${WP_SRC_DIR}"
wget https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
mv latest.tar.gz "wordpress-$(date '+%Y-%m-%d').tar.gz"
cp -a wordpress/. "${WP_BLOG_DIR}/"

# Configure WordPress
print_status "$GREEN" "Configuring WordPress..."
WP_SECURE_SALTS=$(curl -s https://api.wordpress.org/secret-key/1.1/salt/)

cat > "${WP_BLOG_DIR}/wp-config.php" <<EOF
<?php
define('DB_NAME', '${WP_DB_NAME}');
define('DB_USER', '${WP_DB_ADMIN_USER}');
define('DB_PASSWORD', '${MYSQL_WP_ADMIN_USER_PASSWORD}');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', '');

${WP_SECURE_SALTS}

\$table_prefix = 'wp_';

define('WP_DEBUG', ${WP_DEBUG});
define('WP_POST_REVISIONS', ${WP_POST_REVISIONS});
define('AUTOMATIC_UPDATER_DISABLED', false);
define('WP_AUTO_UPDATE_CORE', '${WP_AUTO_UPDATE_CORE}');
define('WP_MEMORY_LIMIT', '${WP_MEMORY_LIMIT}');
define('WP_MAX_MEMORY_LIMIT', '${WP_MAX_MEMORY_LIMIT}');

if ( ! defined('ABSPATH') ) {
    define('ABSPATH', __DIR__ . '/');
}

require_once ABSPATH . 'wp-settings.php';
EOF

# Set correct permissions
print_status "$GREEN" "Setting permissions..."
chown -R www-data:www-data "${WP_ROOT}"
find "${WP_BLOG_DIR}" -type d -exec chmod 755 {} \;
find "${WP_BLOG_DIR}" -type f -exec chmod 644 {} \;

# Final status
print_status "$GREEN" "WordPress installation complete!"
echo "
Installation Summary:
--------------------
WordPress URL: https://${HOSTNAME}
WordPress directory: ${WP_BLOG_DIR}
Database name: ${WP_DB_NAME}
Database user: ${WP_DB_ADMIN_USER}
"

Cleanup Script

#!/bin/bash

# Exit on error
set -e

# Strict mode
set -euo pipefail
IFS=$'\n\t'

# Configuration variables from the installation script
HOSTNAME="aisolutions.com"
WP_ROOT="/var/www/html/wordpress"
NGINX_CONFIG_DIR="/etc/nginx/sites-available"
WP_DB_NAME="aisolution_db"
WP_DB_ADMIN_USER="siddhant"
MYSQL_ROOT_PASSWORD="YourStr0ng!RootPass"

# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

print_status() {
    local color=$1
    local message=$2
    echo -e "${color}${message}${NC}"
}

# Check if script is run as root
if [[ "$EUID" -ne 0 ]]; then
    print_status "$RED" "Error: Please run as root with sudo."
    exit 1
fi

print_status "$GREEN" "Starting cleanup process..."

# Step 1: Remove WordPress Files
if [[ -d "${WP_ROOT}" ]]; then
    print_status "$GREEN" "Removing WordPress files..."
    rm -rf "${WP_ROOT}"
else
    print_status "$YELLOW" "WordPress files not found. Skipping..."
fi

# Step 2: Remove Nginx Configuration
if [[ -f "${NGINX_CONFIG_DIR}/${HOSTNAME}" ]]; then
    print_status "$GREEN" "Removing Nginx configuration..."
    rm -f "${NGINX_CONFIG_DIR}/${HOSTNAME}" /etc/nginx/sites-enabled/${HOSTNAME}
    nginx -t && systemctl reload nginx || print_status "$RED" "Nginx reload failed. Please check manually."
else
    print_status "$YELLOW" "Nginx configuration not found. Skipping..."
fi

# Step 3: Remove MySQL Database and User
print_status "$GREEN" "Removing MySQL database and user..."
mysql -u root -p"${MYSQL_ROOT_PASSWORD}" <<EOF
DROP DATABASE IF EXISTS ${WP_DB_NAME};
DROP USER IF EXISTS '${WP_DB_ADMIN_USER}'@'localhost';
FLUSH PRIVILEGES;
EOF

# Step 4: Uninstall PHP and Nginx (Optional)
read -p "Do you want to uninstall PHP and Nginx as well? [y/N]: " uninstall_choice
if [[ "$uninstall_choice" =~ ^[Yy]$ ]]; then
    print_status "$GREEN" "Uninstalling PHP and Nginx..."
    apt-get purge -y php* nginx*
    apt-get autoremove -y
    apt-get autoclean
else
    print_status "$YELLOW" "Skipping PHP and Nginx uninstallation..."
fi

# Step 5: Final Cleanup
print_status "$GREEN" "Performing final cleanup..."
apt-get autoremove -y
apt-get autoclean

print_status "$GREEN" "WordPress and its dependencies have been successfully removed."

How to Run the Script

  1. Save the script as wordpress-uninstall.sh.

  2. Make the script executable:

     chmod +x wordpress-uninstall.sh
    
  3. Run the script as root or with sudo:

     sudo bash wordpress-uninstall.sh
    

What It Does

  1. Removes WordPress files from /var/www/html/wordpress.

  2. Deletes the Nginx configuration for the domain.

  3. Removes the MySQL database and user.

  4. Optionally uninstalls PHP and Nginx.

  5. Cleans up unused dependencies and packages.