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:
- A *NIX server: This guide is tailored for servers running Linux distributions like Ubuntu or Debian.
- Root or sudo access: You need administrative privileges to install and configure software.
- 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 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
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 = 512M
php_admin_value = 60
php_admin_value = 60
php_admin_value = 10000
php_admin_value = 64M
php_admin_value = 64M
php_admin_value = America/New_York
php_admin_flag = 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
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_unicode_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
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} "
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} "
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. Useheavyfor high-traffic sites andlightfor low-traffic sites.<php_version>: The PHP version to use. Supported versions are7.4and8.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:
- Initial Setup:
./script.sh setup
- Add a Domain:
./script.sh add_domain example.com light 7.4
- Remove a Domain:
./script.sh remove_domain example.com
- Automatically Add Predefined Domains:
./script.sh auto_add_domains
- Import a Database:
./script.sh import_database example.com /path/to/database.sql.gz
- Export a Database:
./script.sh export_database example.com /path/to/backup.sql.gz
- 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!
Enjoyed this article?
Show your appreciation with a clap
You might also like
View allComments (0)
No comments yet. Be the first to comment!


