Running Your Own Hosting with a Simple Script on Any *NIX Server

Hosting your own websites on a *NIX server can be an empowering and cost-effective way to manage your web presence. Whether you are hosting a personal blog, an e-commerce site, or a portfolio, having control over your server can provide significant benefits in terms of performance, security, and customization. This guide will walk you through the process of setting up a fully functional hosting environment on a *NIX server using a simple Bash script, this is mainly designed for hosting woocommerce or wordpress sites with high or low traffic, assuming you have 8GB ram with 4 vcpu server.

Introduction

Running your own hosting server may seem daunting at first, but with the right guidance, it can be a straightforward process. This guide is designed to help you set up and manage your own hosting server using a Bash script. The script will handle the installation and configuration of essential services such as Nginx, MariaDB, and PHP, allowing you to focus on your websites rather than server administration.

Prerequisites

Before you begin, ensure that you have the following:

  1. A *NIX server: This guide is tailored for servers running Linux distributions like Ubuntu or Debian.
  2. Root or sudo access: You need administrative privileges to install and configure software.
  3. Basic knowledge of the command line: Familiarity with terminal commands will help you follow this guide.

Step-by-Step Guide

Step 1: Initial Setup

The first step is to prepare your server by updating the package lists and upgrading installed packages. This ensures that you have the latest security updates and software versions.

#!/bin/bash

# Function to perform the initial setup
initial_setup() {
    # Update and upgrade the system
    sudo apt update && sudo apt upgrade -y

    # Install necessary packages
    sudo apt install -y nginx mariadb-server mariadb-client software-properties-common apt-transport-https lsb-release ca-certificates curl

    # Add Sury PHP repository
    sudo curl -fsSL https://packages.sury.org/php/apt.gpg | sudo gpg --dearmor -o /usr/share/keyrings/php-archive-keyring.gpg
    echo "deb [signed-by=/usr/share/keyrings/php-archive-keyring.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php.list

    # Update repository index
    sudo apt update

    # Install PHP versions
    sudo apt install -y php8.0 php8.0-fpm php8.0-mysql php8.0-cli php8.0-curl php8.0-gd php8.0-mbstring php8.0-xml php8.0-zip
    sudo apt install -y php7.4 php7.4-fpm php7.4-mysql php7.4-cli php7.4-curl php7.4-gd php7.4-mbstring php7.4-xml php7.4-zip

    # Start and enable services
    sudo systemctl start nginx
    sudo systemctl enable nginx

    sudo systemctl start mariadb
    sudo systemctl enable mariadb

    # Secure MariaDB installation
    sudo mysql_secure_installation

    # Adjust Nginx configuration
    sudo sed -i 's/# server_names_hash_bucket_size/server_names_hash_bucket_size 64;/' /etc/nginx/nginx.conf

    # Optimize Nginx for performance
    sudo cat <<EOL | sudo tee /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 1024;
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;
    client_max_body_size 100M;

    # Gzip settings
    gzip on;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # Cache settings
    open_file_cache max=1000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging Settings
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
EOL

    # Create SSL directory
    sudo mkdir -p /etc/nginx/ssl

    # Set timezone and enable NTP
    sudo timedatectl set-timezone America/New_York
    sudo timedatectl set-ntp on
    timedatectl

    # Optimize MariaDB for performance
    sudo cat <<EOL | sudo tee /etc/mysql/mariadb.conf.d/99-custom.cnf

[mysqld]

innodb_buffer_pool_size=4G innodb_log_file_size=1G innodb_flush_log_at_trx_commit=2 innodb_thread_concurrency=8 query_cache_size=64M query_cache_limit=2M max_connections=200 tmp_table_size=64M max_heap_table_size=64M EOL # Restart services to apply changes sudo systemctl restart nginx if [ $? -ne 0 ]; then echo “Failed to restart Nginx. Checking configuration…” sudo nginx -t echo “Please check the error logs and fix the issues before retrying.” exit 1 fi sudo systemctl restart mariadb echo “Initial setup and optimizations completed.” } initial_setup

Step 2: Adding a Domain

Adding a new domain involves creating a directory structure, generating SSL certificates, configuring Nginx, and setting up PHP-FPM. The script below simplifies this process:

# Function to add a domain
add_domain() {
    DOMAIN=$1
    SITE_TYPE=$2
    PHP_VERSION=$3

    # Check if PHP version is valid
    check_php_version $PHP_VERSION

    # Create directory for the domain
    sudo mkdir -p /home/$DOMAIN/htdocs
    sudo chown -R www-data:www-data /home/$DOMAIN/htdocs
    sudo chmod -R 755 /home/$DOMAIN

    # Generate self-signed SSL certificate if not exists
    SSL_DIR="/etc/nginx/ssl"
    SSL_CERT="$SSL_DIR/$DOMAIN.crt"
    SSL_KEY="$SSL_DIR/$DOMAIN.key"

    if [ ! -f "$SSL_CERT" ] || [ ! -f "$SSL_KEY" ]; then
        sudo mkdir -p $SSL_DIR
        sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout $SSL_KEY -out $SSL_CERT -subj "/C=US/ST=State/L=City/O=Organization/OU=Department/CN=$DOMAIN"
        echo "Self-signed SSL certificate generated for $DOMAIN"
    fi

    # Create a new Nginx configuration file for the domain
    NGINX_CONF="/etc/nginx/sites-available/$DOMAIN"
    sudo cat <<EOL | sudo tee $NGINX_CONF
server {
    listen 80;
    server_name $DOMAIN www.$DOMAIN;

    root /home/$DOMAIN/htdocs;
    index index.php index.html index.htm;

    location / {
        try_files \$uri \$uri/ /index.php?\$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php$PHP_VERSION-fpm-$DOMAIN.sock;
    }

    location ~ /\.ht {
        deny all;
    }

    listen 443 ssl;
    ssl_certificate $SSL_CERT;
    ssl_certificate_key $SSL_KEY;
}

EOL

    # Enable the new site
    sudo ln -s $NGINX_CONF /etc/nginx/sites-enabled/

    # Create custom PHP configuration for the domain
    PHP_CONF_DIR="/etc/php/$PHP_VERSION/fpm/pool.d"
    PHP_CONF_FILE="$PHP_CONF_DIR/$DOMAIN.conf"

    # Determine the number of PHP-FPM workers based on site type
    if [ "$SITE_TYPE" == "heavy" ]; then
        PM_MAX_CHILDREN=10
        PM_START_SERVERS=4
        PM_MIN_SPARE_SERVERS=2
        PM_MAX_SPARE_SERVERS=6
    elif [ "$SITE_TYPE" == "light" ]; then
        PM_MAX_CHILDREN=5
        PM_START_SERVERS=2
        PM_MIN_SPARE_SERVERS=1
        PM_MAX_SPARE_SERVERS=3
    else
        echo "Invalid site type. Use 'heavy' or 'light'."
        exit 1
    fi

    sudo cat <<EOL | sudo tee $PHP_CONF_FILE
[$DOMAIN]
user = www-data
group = www-data
listen = /var/run/php/php$PHP_VERSION-fpm-$DOMAIN.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = $PM_MAX_CHILDREN
pm.start_servers = $PM_START_SERVERS
pm.min_spare_servers = $PM_MIN_SPARE_SERVERS
pm.max_spare_servers

 = $PM_MAX_SPARE_SERVERS
chdir = /

php_admin_value[memory_limit] = 512M
php_admin_value[max_execution_time] = 60
php_admin_value[max_input_time] = 60
php_admin_value[max_input_vars] = 10000
php_admin_value[post_max_size] = 64M
php_admin_value[upload_max_filesize] = 64M
php_admin_value[date.timezone] = America/New_York
php_admin_flag[display_errors] = off
EOL

    # Restart PHP-FPM service to apply the new configuration
    sudo systemctl restart php$PHP_VERSION-fpm

    # Test Nginx configuration
    sudo nginx -t
    if [ $? -ne 0 ]; then
        echo "Nginx configuration test failed. Please check the configuration file."
        exit 1
    fi

    # Create a new database for the domain
    DB_NAME=$(echo $DOMAIN | tr -d '.')
    DB_USER="${DB_NAME}_user"
    DB_PASS=$(openssl rand -base64 12)

    sudo mysql --defaults-extra-file=$TEMP_MY_CNF -e "CREATE DATABASE $DB_NAME;"
    sudo mysql --defaults-extra-file=$TEMP_MY_CNF -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';"
    sudo mysql --defaults-extra-file=$TEMP_MY_CNF -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';"
    sudo mysql --defaults-extra-file=$TEMP_MY_CNF -e "FLUSH PRIVILEGES;"

    # Print database credentials
    echo "Database name: $DB_NAME"
    echo "Database user: $DB_USER"
    echo "Database password: $DB_PASS"

    # Restart Nginx to apply changes
    sudo systemctl restart nginx
    if [ $? -ne 0 ]; then
        echo "Failed to restart Nginx. Please check the status of the Nginx service."
        exit 1
    fi

    echo "Domain $DOMAIN added successfully."
}

Step 3: Removing a Domain

Removing a domain involves deleting the Nginx and PHP-FPM configurations, the domain’s directory, and the associated database. Here’s how you can automate this process:

# Function to remove a domain
remove_domain() {
    if [ "$#" -ne 1 ]; then
        echo "Usage: $0 remove_domain domain_name"
        exit 1
    fi

    DOMAIN=$1

    # Remove Nginx configuration
    NGINX_CONF="/etc/nginx/sites-available/$DOMAIN"
    if [ -f $NGINX_CONF ]; then
        sudo rm $NGINX_CONF
        echo "Removed $NGINX_CONF"
    fi

    # Remove the symlink in sites-enabled
    NGINX_SYMLINK="/etc/nginx/sites-enabled/$DOMAIN"
    if [ -L $NGINX_SYMLINK ]; then
        sudo rm $NGINX_SYMLINK
        echo "Removed $NGINX_SYMLINK"
    fi

    # Remove PHP-FPM configuration
    PHP_CONF_DIR="/etc/php"
    for version in 7.4 8.0; do
        PHP_CONF_FILE="$PHP_CONF_DIR/$version/fpm/pool.d/$DOMAIN.conf"
        if [ -f $PHP_CONF_FILE ]; then
            sudo rm $PHP_CONF_FILE
            echo "Removed $PHP_CONF_FILE"
        fi
    done

    # Remove domain directory
    DOMAIN_DIR="/home/$DOMAIN"
    if [ -d $DOMAIN_DIR ]; then
        sudo rm -rf $DOMAIN_DIR
        echo "Removed $DOMAIN_DIR"
    fi

    # Remove the database and user
    DB_NAME=$(echo $DOMAIN | tr -d '.')
    DB_USER="${DB_NAME}_user"
    sudo mysql --defaults-extra-file=$TEMP_MY_CNF -e "DROP DATABASE IF EXISTS $DB_NAME;"
    sudo mysql --defaults-extra-file=$TEMP_MY_CNF -e "DROP USER IF EXISTS '$DB_USER'@'localhost';"
    sudo mysql --defaults-extra-file=$TEMP_MY_CNF -e "FLUSH PRIVILEGES;"
    echo "Removed database and user for $DOMAIN"

    # Restart Nginx to apply changes
    sudo systemctl restart nginx
    if [ $? -ne 0 ]; then
        echo "Failed to restart Nginx. Please check the status of the Nginx service."
        exit 1
    fi

    echo "Domain $DOMAIN removed successfully."
}

Step 4: Managing Databases

You can also automate the import and export of databases. This can be useful for backups or migrations.

Import Database

# Function to import a database
import_database() {
    if [ "$#" -ne 2 ]; then
        echo "Usage: $0 import_database domain_name sql_gz_file_path"
        exit 1
    fi

    DOMAIN=$1
    SQL_GZ_FILE=$2

    # Ensure the SQL file exists
    if [ ! -f "$SQL_GZ_FILE" ]; then
        echo "The file $SQL_GZ_FILE does not exist."
        exit 1
    fi

    # Prompt for MySQL root password
    read -sp "Enter MySQL root password: " MYSQL_ROOT_PASSWORD
    echo

    # Create a temporary MySQL credentials file
    TEMP_MY_CNF=$(mktemp)
    chmod 600 $TEMP_MY_CNF
    cat <<EOF > $TEMP_MY_CNF

[client]

user=root password=$MYSQL_ROOT_PASSWORD EOF # Decompress the SQL file SQL_FILE=”${SQL_GZ_FILE%.gz}” gzip -dc “$SQL_GZ_FILE” > “$SQL_FILE” # Replace collation in the SQL file if needed sed -i ‘s/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g’ “$SQL_FILE” # Import the SQL file into the database DB_NAME=$(echo $DOMAIN | tr -d ‘.’) sudo mysql –defaults-extra-file=$TEMP_MY_CNF $DB_NAME < “$SQL_FILE” # Clean up by removing the decompressed SQL file rm “$SQL_FILE” rm $TEMP_MY_CNF echo “Database imported for $DOMAIN successfully.” # Set permissions for the WordPress directory setperm $DOMAIN }

Export Database

# Function to export a database
export_database() {
    if [ "$#" -ne 2 ]; then
        echo "Usage: $0 export_database domain_name output_sql_gz_file_path"
        exit 1
    fi

    DOMAIN=$1
    OUTPUT_SQL_GZ_FILE=$2

    # Prompt for MySQL root password
    read -sp "Enter MySQL root password: " MYSQL_ROOT_PASSWORD
    echo

    # Create a temporary MySQL credentials file
    TEMP_MY_CNF=$(mktemp)
    chmod 600 $TEMP_MY_CNF
    cat <<EOF > $TEMP_MY_CNF

[client]

user=root password=$MYSQL_ROOT_PASSWORD EOF # Export the database to a SQL file DB_NAME=$(echo $DOMAIN | tr -d ‘.’) SQL_FILE=”${OUTPUT_SQL_GZ_FILE%.gz}” sudo mysqldump –defaults-extra-file=$TEMP_MY_CNF $DB_NAME > “$SQL_FILE” # Compress the SQL file gzip “$SQL_FILE” # Clean up by removing the temporary MySQL credentials file rm $TEMP_MY_CNF echo “Database exported for $DOMAIN successfully to $OUTPUT_SQL_GZ_FILE.” }

Step 5: Setting Permissions

Setting the correct permissions for your WordPress directories is crucial for security and functionality.

# Function to set permissions for a WordPress directory
setperm() {
    DOMAIN=$1
    WP_PATH="/home/$DOMAIN/htdocs"

    # Ensure the directory exists
    if [ ! -d "$WP_PATH" ]; then
        echo "The directory $WP_PATH does not exist."
        exit 1
    fi

    # Set ownership to www-data
    echo "Setting ownership to www-data for $WP_PATH..."
    sudo chown -R www-data:www-data $WP_PATH

    # Set permissions for directories and files in one command for better performance
    echo "Setting permissions for $WP_PATH..."
    sudo find $WP_PATH -type d -print0 | xargs -0 chmod 755
    sudo find $WP_PATH -type f -print0 | xargs -0 chmod 644

    # Set wp-config.php permissions to 600
    if [ -f "$WP_PATH/wp-config.php" ]; then
        echo "Setting wp-config.php permissions to 600..."
        sudo chmod 600 $WP_PATH/wp-config.php
    else
        echo "wp-config.php not found in $WP_PATH. Skipping..."
    fi

    echo "Permissions have been set for $WP_PATH."
}

Main Script

Here’s the main script that ties all the functions together:

# Main script
if [ "$1" == "setup" ]; then
    initial_setup
elif [ "$1" == "add_domain" ]; then
    shift
    add_domain "$@"
elif [ "$1" == "remove_domain" ]; then
    shift
    remove_domain "$@"
elif [ "$1" == "auto_add_domains" ]; then
    auto_add_domains
elif [ "$1" == "import_database" ]; then
    shift
    import_database "$@"


elif [ "$1" == "export_database" ]; then
    shift
    export_database "$@"
elif [ "$1" == "setperm" ]; then
    shift
    setperm "$@"
else
    echo "Usage: $0 {setup|add_domain|remove_domain|auto_add_domains|import_database|export_database|setperm} [arguments]"
    exit 1
fi

Let’s break down the usage of the script and explain each command and its arguments:

echo "Usage: $0 {setup|add_domain|remove_domain|auto_add_domains|import_database|export_database|setperm} [arguments]"

Script Usage

The script supports several commands to manage your hosting environment. Here’s a brief explanation of each command and how to use it:

1. setup

Usage: ./script.sh setup

  • Purpose: Perform the initial setup of your server. This includes updating the system, installing necessary packages (Nginx, MariaDB, PHP), securing MariaDB, and configuring Nginx and MariaDB for optimal performance.
  • Arguments: None.

2. add_domain

Usage: ./script.sh add_domain <domain_name> <site_type> <php_version>

  • Purpose: Add a new domain to your server. This involves creating a directory structure, generating a self-signed SSL certificate, configuring Nginx, setting up PHP-FPM, and creating a new database.
  • Arguments:
  • <domain_name>: The domain name you want to add (e.g., example.com).
  • <site_type>: The type of site, which determines the PHP-FPM worker configuration. Use heavy for high-traffic sites and light for low-traffic sites.
  • <php_version>: The PHP version to use. Supported versions are 7.4 and 8.0.

3. remove_domain

Usage: ./script.sh remove_domain <domain_name>

  • Purpose: Remove an existing domain from your server. This includes deleting the Nginx and PHP-FPM configurations, the domain’s directory, and the associated database.
  • Arguments:
  • <domain_name>: The domain name you want to remove (e.g., example.com).

4. auto_add_domains

Usage: ./script.sh auto_add_domains

  • Purpose: Automatically add a predefined list of domains to your server. This is useful for quickly setting up multiple domains with specific configurations.
  • Arguments: None.

5. import_database

Usage: ./script.sh import_database <domain_name> <sql_gz_file_path>

  • Purpose: Import a database for a specified domain from a compressed SQL file. This is useful for restoring backups or migrating databases.
  • Arguments:
  • <domain_name>: The domain name associated with the database (e.g., example.com).
  • <sql_gz_file_path>: The path to the compressed SQL file (e.g., /path/to/database.sql.gz).

6. export_database

Usage: ./script.sh export_database <domain_name> <output_sql_gz_file_path>

  • Purpose: Export the database of a specified domain to a compressed SQL file. This is useful for creating backups or migrating databases.
  • Arguments:
  • <domain_name>: The domain name associated with the database (e.g., example.com).
  • <output_sql_gz_file_path>: The path to save the compressed SQL file (e.g., /path/to/backup.sql.gz).

7. setperm

Usage: ./script.sh setperm <domain_name>

  • Purpose: Set the correct permissions for a WordPress directory associated with a specified domain. This ensures that the web server can read and write files as needed while maintaining security.
  • Arguments:
  • <domain_name>: The domain name whose directory permissions you want to set (e.g., example.com).

Example Commands

Here are a few examples of how to use the script:

  1. Initial Setup:
   ./script.sh setup
  1. Add a Domain:
   ./script.sh add_domain example.com light 7.4
  1. Remove a Domain:
   ./script.sh remove_domain example.com
  1. Automatically Add Predefined Domains:
   ./script.sh auto_add_domains
  1. Import a Database:
   ./script.sh import_database example.com /path/to/database.sql.gz
  1. Export a Database:
   ./script.sh export_database example.com /path/to/backup.sql.gz
  1. Set Permissions for a Domain:
   ./script.sh setperm example.com

With these commands, you can efficiently manage your hosting environment on any *NIX server. This script simplifies the process of setting up, configuring, and maintaining your web hosting services.

Conclusion

Setting up your own hosting server can be a rewarding experience. With this comprehensive guide and the provided Bash script, you can easily manage your server, add or remove domains, and handle database operations with ease. The power of automation allows you to focus more on developing your websites and less on the intricacies of server management. Happy hosting!

Leave a Reply

Your email address will not be published. Required fields are marked *