How much does a WordPress website cost?

2»

Comments

  • bikegremlinbikegremlin ModeratorOGContent Writer

    @somik said:

    @bikegremlin said:
    Sums it up well - though my eye starts twitching when I hear "Bluehost" - LOL :)

    Who here, on the low end sites, have never used hosts like bluehost, 25mb host, 000webhosting and all those crappy sites?

    Yup.
    bikegremlin.com was first registered with 1-and-1 for $1 for the first year (then I read the terms, saw the renewal price, and moved it in time to Namecheap, and a few years after that to Porkbun).

    Hosting was first with a suspiciously cheap shared hosting provider (unlimited all LOL), that got sold to Justhost (which was owned by the Bluehost umbrella company). And it took a while to figure out how the hosting is not very good.

    It took me a while to realize that time is the most limited resource and that I should pay a bit more to not worry about hosting, and spend the time (and effort) on writing articles.

    Similar goes for WordPress itself - while it is free and open source, it has its costs in terms of both time and money, depending on what you're using it for.

  • vyasvyas OGSenpai
    edited August 2025

    Thanks**

    To this discussion, I spent a couple of hours with chatGPT and grok last night, while visiting a family member at an assisted living facility. (Story for another time)...

    I now have hopefully working scripts for installing ClassicPress (or WordPress if you prefer) on Debian, using Caddy/ Lighttpd and Openlitespeed as the "control" for LES Budget friendly specs.

    Idea came up when I asked chatGPT "why do installer scripts like Webinoly only support Ubuntu? Why can't i get something with Debian 12 (or 13) ? Pasting the "optimized version" of the conversation and the script below. As always, comments and feedback appreciated. I might do an actual testing later in the day.

    What this script is supposed to do:

    • Support Interactive or Defaults mode.

    • Support Dry-run mode (simulate everything; do not change the system).

    • Run pre-flight checks (OS, RAM, CPU, free disk) and warn if low — but allow continuing.

    • Prompt for domain, admin user/email/password (defaults if Enter), install path (default /var/www/classicpress), DB prefix (default cp1_).

    • Install only required packages (skip if already installed) and show progress messages.

    • Install WP-CLI, download ClassicPress, set permissions, generate wp-config.php via WP-CLI, run the ClassicPress install non-interactively.

    • Install optional plugins/themes via WP-CLI (defaults pre-set but editable).

    • Configure SSL:

    • Caddy: automatic (Caddy handles TLS once DNS points).

    • OLS: uses certbot to obtain cert (script checks DNS).

    • Print final summary (login URL, admin creds, DB creds, paths).

    • Plenty of inline comments and terminal output so you can follow what is happening.

    Note: these scripts attempt to be conservative and safe. Always review before running on a production machine. Run first in --dry-run to verify.

    The Script itself: simplenote link - http://simp.ly/p/KMkQVp

    Or read below at your own scrolling risk:

    Example: Classic Press + Caddy

    Save as install_cp_caddy.sh, then
    $ chmod +x install_cp_caddy.sh, and finally, run with
    $ sudo ./install_cp_caddy.sh.


    <

    pre>

    !/usr/bin/env bash

    set -euo pipefail
    IFS=$'\n\t'

    -------------------------

    Defaults & Configurable Vars

    -------------------------

    DEFAULT_DOMAIN="example.com"
    DEFAULT_ADMIN_USER="admin"
    DEFAULT_ADMIN_EMAIL="[email protected]"
    DEFAULT_INSTALL_FOLDER="classicpress"
    DEFAULT_WEBROOT_BASE="/var/www"
    DEFAULT_DB_PREFIX="cp1"
    PHP_VERSION="8.3"
    MIN_DISK_GB=5
    MIN_RAM_MB=1024

    Default plugin/theme lists (empty by default; edit if you want auto-install)

    DEFAULT_PLUGINS=("classic-editor") # Example; add slugs you want
    DEFAULT_THEMES=()

    -------------------------

    Helper functions

    -------------------------

    log() { echo -e "\e[1;34m[ INFO ]\e[0m $"; }
    warn() { echo -e "\e[1;33m[ WARN ]\e[0m $
    "; }
    err() { echo -e "\e[1;31m[ ERROR ]\e[0m $*"; }

    confirm() {
    # prompt with default no
    read -p "$1 (y/N): " ans
    [[ "$ans" == "y" || "$ans" == "Y" ]]
    }

    command_exists() { command -v "$1" >/dev/null 2>&1; }

    -------------------------

    CLI flags: --dry-run

    -------------------------

    DRY_RUN=false
    while [[ $# -gt 0 ]]; do
    case $1 in
    --dry-run) DRY_RUN=true; shift ;;
    --help) echo "Usage: sudo $0 [--dry-run]"; exit 0 ;;
    *) echo "Unknown arg: $1"; exit 1 ;;
    esac
    done

    -------------------------

    Mode selection: interactive or defaults

    -------------------------

    echo "ClassicPress + Caddy Installer"
    echo "Interactive or defaults?"
    echo " 1) Interactive (step-by-step prompts)"
    echo " 2) Defaults (use sensible defaults, minimal prompts)"
    read -p "Choose mode [1-2, default 1]: " MODE_CHOICE
    MODE_CHOICE=${MODE_CHOICE:-1}

    INTERACTIVE=true
    if [[ "$MODE_CHOICE" == "2" ]]; then
    INTERACTIVE=false
    fi

    Dry-run message

    if $DRY_RUN; then
    log "DRY RUN mode enabled — no changes will be made."
    fi

    -------------------------

    Pre-flight checks

    -------------------------

    log "Running pre-flight checks..."

    OS check (Debian)

    if [ -f /etc/debian_version ]; then
    OS_NAME="Debian"
    OS_VER=$(cat /etc/debian_version)
    log "Detected OS: $OS_NAME $OS_VER"
    else
    err "This installer is intended for Debian. Aborting."
    exit 1
    fi

    Hardware checks (warn, not abort)

    TOTAL_RAM_MB=$(free -m | awk '/^Mem:/{print $2}')
    FREE_DISK_GB=$(df -BG --output=avail / | tail -1 | tr -dc '0-9')
    CPU_CORES=$(nproc)

    if (( TOTAL_RAM_MB < MIN_RAM_MB )); then
    warn "Detected RAM: ${TOTAL_RAM_MB} MB — recommended >= ${MIN_RAM_MB} MB."
    if ! confirm "Proceed anyway?"; then
    log "User aborted due to low RAM."
    exit 1
    fi
    fi

    if (( FREE_DISK_GB < MIN_DISK_GB )); then
    warn "Detected free disk: ${FREE_DISK_GB} GB — recommended >= ${MIN_DISK_GB} GB."
    if ! confirm "Proceed anyway?"; then
    log "User aborted due to low disk."
    exit 1
    fi
    fi

    log "CPU cores: $CPU_CORES; RAM: ${TOTAL_RAM_MB} MB; Free disk: ${FREE_DISK_GB} GB"

    -------------------------

    Interactive prompts (or defaults)

    -------------------------

    if $INTERACTIVE; then
    read -p "Enter domain (FQDN) [${DEFAULT_DOMAIN}]: " DOMAIN
    DOMAIN=${DOMAIN:-$DEFAULT_DOMAIN}
    read -p "Install folder name [${DEFAULT_INSTALL_FOLDER}]: " FOLDER_NAME
    FOLDER_NAME=${FOLDER_NAME:-$DEFAULT_INSTALL_FOLDER}
    read -p "Admin username [${DEFAULT_ADMIN_USER}]: " ADMIN_USER
    ADMIN_USER=${ADMIN_USER:-$DEFAULT_ADMIN_USER}
    read -p "Admin email [${DEFAULT_ADMIN_EMAIL}]: " ADMIN_EMAIL
    ADMIN_EMAIL=${ADMIN_EMAIL:-$DEFAULT_ADMIN_EMAIL}
    read -p "Admin password (leave blank to auto-generate): " ADMIN_PASS
    if [[ -z "$ADMIN_PASS" ]]; then
    ADMIN_PASS=$(openssl rand -base64 12)
    log "Generated admin password: $ADMIN_PASS"
    fi
    read -p "DB prefix [${DEFAULT_DB_PREFIX}]: " DB_PREFIX
    DB_PREFIX=${DB_PREFIX:-$DEFAULT_DB_PREFIX}
    read -p "Use MariaDB (y) or SQLite (n)? [y]: " DB_CHOICE
    DB_CHOICE=${DB_CHOICE:-y}
    if [[ "$DB_CHOICE" == "y" || "$DB_CHOICE" == "Y" ]]; then
    DB_ENGINE="mariadb"
    else
    DB_ENGINE="sqlite"
    fi
    read -p "Install default plugins? (classic-editor) [y/N]: " INSTALL_PLUGINS_ANS
    INSTALL_PLUGINS=false
    if [[ "$INSTALL_PLUGINS_ANS" == "y" || "$INSTALL_PLUGINS_ANS" == "Y" ]]; then
    INSTALL_PLUGINS=true
    fi
    else
    # defaults
    DOMAIN=$DEFAULT_DOMAIN
    FOLDER_NAME=$DEFAULT_INSTALL_FOLDER
    ADMIN_USER=$DEFAULT_ADMIN_USER
    ADMIN_EMAIL=$DEFAULT_ADMIN_EMAIL
    ADMIN_PASS=$(openssl rand -base64 12)
    DB_PREFIX=$DEFAULT_DB_PREFIX
    DB_ENGINE="mariadb"
    INSTALL_PLUGINS=false
    fi

    WEBROOT="${DEFAULT_WEBROOT_BASE}/${FOLDER_NAME}"
    DB_NAME="${DB_PREFIX}db"
    DB_USER="${DB_PREFIX}user"
    DB_PASS=$(openssl rand -base64 16)

    log "Summary of choices:"
    echo " Domain: $DOMAIN"
    echo " Webroot: $WEBROOT"
    echo " Admin: $ADMIN_USER / $ADMIN_EMAIL"
    echo " DB engine: $DB_ENGINE"
    echo " DB name/user: $DB_NAME / $DB_USER"
    echo " Install plugins/themes: $INSTALL_PLUGINS"
    echo " PHP version target: $PHP_VERSION"

    if $DRY_RUN; then
    echo
    log "DRY RUN summary complete. No changes will be made."
    exit 0
    fi

    -------------------------

    Install prerequisites (only what is missing)

    -------------------------

    log "Installing prerequisites (only missing packages)..."

    Add Sury repo for PHP 8.3 if needed

    if ! command_exists php || [[ "$(php -r 'echo PHP_VERSION;')" != "$PHP_VERSION"* ]]; then
    log "Installing PHP ${PHP_VERSION} and extensions..."
    apt update
    apt install -y ca-certificates apt-transport-https lsb-release gnupg
    echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/sury-php.list
    wget -qO - https://packages.sury.org/php/apt.gpg | gpg --dearmor -o /usr/share/keyrings/sury-php.gpg
    apt update
    apt install -y "php${PHP_VERSION}" "php${PHP_VERSION}-fpm" "php${PHP_VERSION}-mysql" \
    "php${PHP_VERSION}-xml" "php${PHP_VERSION}-mbstring" "php${PHP_VERSION}-curl" \
    "php${PHP_VERSION}-zip" "php${PHP_VERSION}-gd" unzip wget curl
    else
    log "PHP ${PHP_VERSION} appears installed."
    fi

    Caddy installation (official)

    if ! command_exists caddy; then
    log "Installing Caddy..."
    apt install -y debian-keyring debian-archive-keyring apt-transport-https
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable.gpg
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
    apt update
    apt install -y caddy
    else
    log "Caddy already installed."
    fi

    Database

    if [[ "$DB_ENGINE" == "mariadb" ]]; then
    if ! command_exists mysql; then
    log "Installing MariaDB..."
    apt install -y mariadb-server mariadb-client
    log "Securing MariaDB (interactive may appear)..."
    # attempt non-interactive secure install: set root auth to unix_socket (Debian default)
    # create DB and user later
    else
    log "MariaDB present."
    fi
    else
    log "SQLite selected; PHP sqlite extension will be used."
    apt install -y "php${PHP_VERSION}-sqlite3"
    fi

    WP-CLI

    if ! command_exists wp; then
    log "Installing wp-cli..."
    curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
    php wp-cli.phar --info >/dev/null 2>&1 || true
    chmod +x wp-cli.phar
    mv wp-cli.phar /usr/local/bin/wp
    else
    log "wp-cli present."
    fi

    -------------------------

    Database setup

    -------------------------

    if [[ "$DB_ENGINE" == "mariadb" ]]; then
    log "Creating database and user..."
    mysql -e "CREATE DATABASE IF NOT EXISTS `${DB_NAME}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
    mysql -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';"
    mysql -e "GRANT ALL PRIVILEGES ON &#96;${DB_NAME}&#96;.* TO '${DB_USER}'@'localhost';"
    mysql -e "FLUSH PRIVILEGES;"
    log "Database ${DB_NAME} and user ${DB_USER} created."
    fi

    -------------------------

    Download and place ClassicPress

    -------------------------

    log "Downloading ClassicPress..."
    mkdir -p "$WEBROOT"
    cd /tmp
    CP_RELEASE_URL="https://github.com/ClassicPress/ClassicPress-release/archive/refs/tags/1.6.3.tar.gz"
    wget -qO cp.tar.gz "$CP_RELEASE_URL"
    tar -xzf cp.tar.gz
    shopt -s dotglob
    mv ClassicPress-release-/ "$WEBROOT"/
    rm -rf /tmp/ClassicPress-release-* cp.tar.gz
    shopt -u dotglob

    log "Setting ownership and permissions..."
    chown -R www-data:www-data "$WEBROOT"
    find "$WEBROOT" -type d -exec chmod 755 {} \;
    find "$WEBROOT" -type f -exec chmod 644 {} \;

    -------------------------

    Caddy config

    -------------------------

    log "Creating Caddyfile for ${DOMAIN}..."
    cat > /etc/caddy/Caddyfile <<EOF
    ${DOMAIN} {
    root * ${WEBROOT}
    encode gzip zstd
    php_fastcgi unix//run/php/php${PHP_VERSION}-fpm.sock
    file_server
    }
    EOF

    log "Reloading Caddy..."
    systemctl reload caddy || systemctl restart caddy

    -------------------------

    ClassicPress install via WP-CLI

    -------------------------

    log "Preparing wp-config.php and running site install..."

    Create wp-config.php via wp-cli using DB engine chosen

    if [[ "$DB_ENGINE" == "mariadb" ]]; then
    wp config create --path="$WEBROOT" --dbname="$DB_NAME" --dbuser="$DB_USER" --dbpass="$DB_PASS" --dbhost="localhost" --dbprefix="${DB_PREFIX}_" --skip-check --allow-root
    else
    # use SQLite db.php drop-in approach
    # ensure php sqlite extension present
    WP_DB_DIR="$WEBROOT/wp-content/database"
    mkdir -p "$WP_DB_DIR"
    chown -R www-data:www-data "$WP_DB_DIR"
    # download db.php drop-in if available (wp-sqlite-db)
    wget -qO "$WEBROOT/wp-content/db.php" "https://raw.githubusercontent.com/aaemnnosttv/wp-sqlite-db/v1.3.2/src/db.php"
    wp config create --path="$WEBROOT" --skip-check --allow-root
    fi

    Install the site

    wp core install --path="$WEBROOT" --url="https://${DOMAIN}" --title="ClassicPress Site" --admin_user="${ADMIN_USER}" --admin_password="${ADMIN_PASS}" --admin_email="${ADMIN_EMAIL}" --skip-email --allow-root

    log "Installing default plugins/themes if requested..."
    if $INSTALL_PLUGINS; then
    for pl in "${DEFAULT_PLUGINS[@]}"; do
    wp plugin install "$pl" --activate --path="$WEBROOT" --allow-root || warn "Failed to install plugin $pl"
    done
    fi
    for th in "${DEFAULT_THEMES[@]}"; do
    wp theme install "$th" --activate --path="$WEBROOT" --allow-root || warn "Failed to install theme $th"
    done

    -------------------------

    SSL (Caddy auto handles TLS) and reminder

    -------------------------

    log "Caddy will manage TLS automatically when DNS for ${DOMAIN} points to this server's IP."
    log "If DNS is not yet pointed, TLS will be obtained automatically when it is."

    -------------------------

    Final summary

    -------------------------

    echo

    echo "===================================================="

    echo " ClassicPress install complete (Caddy mode)"
    echo " Domain: https://${DOMAIN}"
    echo " Admin user: ${ADMIN_USER}"
    echo " Admin password: ${ADMIN_PASS}"
    if [[ "$DB_ENGINE" == "mariadb" ]]; then
    echo " DB: ${DB_NAME} / ${DB_USER} / ${DB_PASS}"
    else
    echo " DB: SQLite (file stored in wp-content/database by default)"
    fi
    echo " Webroot: ${WEBROOT}"
    echo " Caddyfile: /etc/caddy/Caddyfile"
    echo " WP-CLI: /usr/local/bin/wp"

    echo "===================================================="

    Note

    1. ** (or not - if you ask my wife)

    2. The usual questions came to mind- why not github repo/ Why not use pastebin, etc... before I posted the script here. Anyways, formatting in vanilla drove me nuts.

    3. Since much of the code was AI tested/ suggested, not sure about license either. Lets' call it a bored middle aged man project license.
      (gosh, am I really that old? OR MAybe.. use it ONLY if you are that old..lol)

    4. Sorry @bikegremlin, did not meant to hijack thread.

  • bikegremlinbikegremlin ModeratorOGContent Writer

    @vyas said:
    Thanks**

    To this discussion, I spent a couple of hours with chatGPT and grok last night, while visiting a family member at an assisted living facility. (Story for another time)...

    I now have hopefully working scripts for installing ClassicPress (or WordPress if you prefer) on Debian, using Caddy/ Lighttpd and Openlitespeed as the "control" for LES Budget friendly specs.

    Idea came up when I asked chatGPT "why do installer scripts like Webinoly only support Ubuntu? Why can't i get something with Debian 12 (or 13) ? Pasting the "optimized version" of the conversation and the script below. As always, comments and feedback appreciated. I might do an actual testing later in the day.

    What this script is supposed to do:

    • Support Interactive or Defaults mode.

    • Support Dry-run mode (simulate everything; do not change the system).

    • Run pre-flight checks (OS, RAM, CPU, free disk) and warn if low — but allow continuing.

    • Prompt for domain, admin user/email/password (defaults if Enter), install path (default /var/www/classicpress), DB prefix (default cp1_).

    • Install only required packages (skip if already installed) and show progress messages.

    • Install WP-CLI, download ClassicPress, set permissions, generate wp-config.php via WP-CLI, run the ClassicPress install non-interactively.

    • Install optional plugins/themes via WP-CLI (defaults pre-set but editable).

    • Configure SSL:

    • Caddy: automatic (Caddy handles TLS once DNS points).

    • OLS: uses certbot to obtain cert (script checks DNS).

    • Print final summary (login URL, admin creds, DB creds, paths).

    • Plenty of inline comments and terminal output so you can follow what is happening.

    Note: these scripts attempt to be conservative and safe. Always review before running on a production machine. Run first in --dry-run to verify.

    The Script itself: simplenote link - http://simp.ly/p/KMkQVp

    Or read below at your own scrolling risk:

    Example: Classic Press + Caddy

    Save as install_cp_caddy.sh, then
    $ chmod +x install_cp_caddy.sh, and finally, run with
    $ sudo ./install_cp_caddy.sh.


    > 
    > ####!/usr/bin/env bash
    > 
    > set -euo pipefail
    > IFS=$'\n\t'
    > 
    > # -------------------------
    > 
    > ##### Defaults & Configurable Vars
    > 
    > # -------------------------
    > 
    > DEFAULT_DOMAIN="example.com"
    > DEFAULT_ADMIN_USER="admin"
    > DEFAULT_ADMIN_EMAIL="[email protected]"
    > DEFAULT_INSTALL_FOLDER="classicpress"
    > DEFAULT_WEBROOT_BASE="/var/www"
    > DEFAULT_DB_PREFIX="cp1"
    > PHP_VERSION="8.3"
    > MIN_DISK_GB=5
    > MIN_RAM_MB=1024
    > 
    > # Default plugin/theme lists (empty by default; edit if you want auto-install)
    > DEFAULT_PLUGINS=("classic-editor")   # Example; add slugs you want
    > DEFAULT_THEMES=()
    > 
    > # -------------------------
    > 
    > ###### Helper functions
    > 
    > # -------------------------
    > log() { echo -e "\e[1;34m[ INFO ]\e[0m $*"; }
    > warn() { echo -e "\e[1;33m[ WARN ]\e[0m $*"; }
    > err() { echo -e "\e[1;31m[ ERROR ]\e[0m $*"; }
    > 
    > confirm() {
    >   # prompt with default no
    >   read -p "$1 (y/N): " ans
    >   [[ "$ans" == "y" || "$ans" == "Y" ]]
    > }
    > 
    > command_exists() { command -v "$1" >/dev/null 2>&1; }
    > 
    > # -------------------------
    > 
    > ##### CLI flags: --dry-run
    > 
    > # -------------------------
    > 
    > DRY_RUN=false
    > while [[ $# -gt 0 ]]; do
    >   case $1 in
    >     --dry-run) DRY_RUN=true; shift ;;
    >     --help) echo "Usage: sudo $0 [--dry-run]"; exit 0 ;;
    >     *) echo "Unknown arg: $1"; exit 1 ;;
    >   esac
    > done
    > 
    > # -------------------------
    > 
    > ##### Mode selection: interactive or defaults
    > 
    > # -------------------------
    > 
    > echo "ClassicPress + Caddy Installer"
    > echo "Interactive or defaults?"
    > echo "  1) Interactive (step-by-step prompts)"
    > echo "  2) Defaults (use sensible defaults, minimal prompts)"
    > read -p "Choose mode [1-2, default 1]: " MODE_CHOICE
    > MODE_CHOICE=${MODE_CHOICE:-1}
    > 
    > INTERACTIVE=true
    > if [[ "$MODE_CHOICE" == "2" ]]; then
    >   INTERACTIVE=false
    > fi
    > 
    > # Dry-run message
    > if $DRY_RUN; then
    >   log "DRY RUN mode enabled — no changes will be made."
    > fi
    > 
    > # -------------------------
    > 
    > ##### Pre-flight checks
    > 
    > # -------------------------
    > 
    > log "Running pre-flight checks..."
    > 
    > #### OS check (Debian)
    > if [ -f /etc/debian_version ]; then
    >   OS_NAME="Debian"
    >   OS_VER=$(cat /etc/debian_version)
    >   log "Detected OS: $OS_NAME $OS_VER"
    > else
    >   err "This installer is intended for Debian. Aborting."
    >   exit 1
    > fi
    > 
    > ##### Hardware checks (warn, not abort)
    > TOTAL_RAM_MB=$(free -m | awk '/^Mem:/{print $2}')
    > FREE_DISK_GB=$(df -BG --output=avail / | tail -1 | tr -dc '0-9')
    > CPU_CORES=$(nproc)
    > 
    > if (( TOTAL_RAM_MB < MIN_RAM_MB )); then
    >   warn "Detected RAM: ${TOTAL_RAM_MB} MB — recommended >= ${MIN_RAM_MB} MB."
    >   if ! confirm "Proceed anyway?"; then
    >     log "User aborted due to low RAM."
    >     exit 1
    >   fi
    > fi
    > 
    > if (( FREE_DISK_GB < MIN_DISK_GB )); then
    >   warn "Detected free disk: ${FREE_DISK_GB} GB — recommended >= ${MIN_DISK_GB} GB."
    >   if ! confirm "Proceed anyway?"; then
    >     log "User aborted due to low disk."
    >     exit 1
    >   fi
    > fi
    > 
    > log "CPU cores: $CPU_CORES; RAM: ${TOTAL_RAM_MB} MB; Free disk: ${FREE_DISK_GB} GB"
    > 
    > # -------------------------
    > 
    > ##### Interactive prompts (or defaults)
    > 
    > # -------------------------
    > 
    > if $INTERACTIVE; then
    >   read -p "Enter domain (FQDN) [${DEFAULT_DOMAIN}]: " DOMAIN
    >   DOMAIN=${DOMAIN:-$DEFAULT_DOMAIN}
    >   read -p "Install folder name [${DEFAULT_INSTALL_FOLDER}]: " FOLDER_NAME
    >   FOLDER_NAME=${FOLDER_NAME:-$DEFAULT_INSTALL_FOLDER}
    >   read -p "Admin username [${DEFAULT_ADMIN_USER}]: " ADMIN_USER
    >   ADMIN_USER=${ADMIN_USER:-$DEFAULT_ADMIN_USER}
    >   read -p "Admin email [${DEFAULT_ADMIN_EMAIL}]: " ADMIN_EMAIL
    >   ADMIN_EMAIL=${ADMIN_EMAIL:-$DEFAULT_ADMIN_EMAIL}
    >   read -p "Admin password (leave blank to auto-generate): " ADMIN_PASS
    >   if [[ -z "$ADMIN_PASS" ]]; then
    >     ADMIN_PASS=$(openssl rand -base64 12)
    >     log "Generated admin password: $ADMIN_PASS"
    >   fi
    >   read -p "DB prefix [${DEFAULT_DB_PREFIX}]: " DB_PREFIX
    >   DB_PREFIX=${DB_PREFIX:-$DEFAULT_DB_PREFIX}
    >   read -p "Use MariaDB (y) or SQLite (n)? [y]: " DB_CHOICE
    >   DB_CHOICE=${DB_CHOICE:-y}
    >   if [[ "$DB_CHOICE" == "y" || "$DB_CHOICE" == "Y" ]]; then
    >     DB_ENGINE="mariadb"
    >   else
    >     DB_ENGINE="sqlite"
    >   fi
    >   read -p "Install default plugins? (classic-editor) [y/N]: " INSTALL_PLUGINS_ANS
    >   INSTALL_PLUGINS=false
    >   if [[ "$INSTALL_PLUGINS_ANS" == "y" || "$INSTALL_PLUGINS_ANS" == "Y" ]]; then
    >     INSTALL_PLUGINS=true
    >   fi
    > else
    >   # defaults
    >   DOMAIN=$DEFAULT_DOMAIN
    >   FOLDER_NAME=$DEFAULT_INSTALL_FOLDER
    >   ADMIN_USER=$DEFAULT_ADMIN_USER
    >   ADMIN_EMAIL=$DEFAULT_ADMIN_EMAIL
    >   ADMIN_PASS=$(openssl rand -base64 12)
    >   DB_PREFIX=$DEFAULT_DB_PREFIX
    >   DB_ENGINE="mariadb"
    >   INSTALL_PLUGINS=false
    > fi
    > 
    > WEBROOT="${DEFAULT_WEBROOT_BASE}/${FOLDER_NAME}"
    > DB_NAME="${DB_PREFIX}db"
    > DB_USER="${DB_PREFIX}user"
    > DB_PASS=$(openssl rand -base64 16)
    > 
    > log "Summary of choices:"
    > echo " Domain: $DOMAIN"
    > echo " Webroot: $WEBROOT"
    > echo " Admin: $ADMIN_USER / $ADMIN_EMAIL"
    > echo " DB engine: $DB_ENGINE"
    > echo " DB name/user: $DB_NAME / $DB_USER"
    > echo " Install plugins/themes: $INSTALL_PLUGINS"
    > echo " PHP version target: $PHP_VERSION"
    > 
    > if $DRY_RUN; then
    >   echo
    >   log "DRY RUN summary complete. No changes will be made."
    >   exit 0
    > fi
    > 
    > # -------------------------
    > 
    > ##### Install prerequisites (only what is missing)
    > 
    > # -------------------------
    > 
    > log "Installing prerequisites (only missing packages)..."
    > ##### Add Sury repo for PHP 8.3 if needed
    > if ! command_exists php || [[ "$(php -r 'echo PHP_VERSION;')" != "$PHP_VERSION"* ]]; then
    >   log "Installing PHP ${PHP_VERSION} and extensions..."
    >   apt update
    >   apt install -y ca-certificates apt-transport-https lsb-release gnupg
    >   echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/sury-php.list
    >   wget -qO - https://packages.sury.org/php/apt.gpg | gpg --dearmor -o /usr/share/keyrings/sury-php.gpg
    >   apt update
    >   apt install -y "php${PHP_VERSION}" "php${PHP_VERSION}-fpm" "php${PHP_VERSION}-mysql" \
    >     "php${PHP_VERSION}-xml" "php${PHP_VERSION}-mbstring" "php${PHP_VERSION}-curl" \
    >     "php${PHP_VERSION}-zip" "php${PHP_VERSION}-gd" unzip wget curl
    > else
    >   log "PHP ${PHP_VERSION} appears installed."
    > fi
    > 
    > ##### Caddy installation (official)
    > if ! command_exists caddy; then
    >   log "Installing Caddy..."
    >   apt install -y debian-keyring debian-archive-keyring apt-transport-https
    >   curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable.gpg
    >   curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
    >   apt update
    >   apt install -y caddy
    > else
    >   log "Caddy already installed."
    > fi
    > 
    > #### Database
    > if [[ "$DB_ENGINE" == "mariadb" ]]; then
    >   if ! command_exists mysql; then
    >     log "Installing MariaDB..."
    >     apt install -y mariadb-server mariadb-client
    >     log "Securing MariaDB (interactive may appear)..."
    >     # attempt non-interactive secure install: set root auth to unix_socket (Debian default)
    >     # create DB and user later
    >   else
    >     log "MariaDB present."
    >   fi
    > else
    >   log "SQLite selected; PHP sqlite extension will be used."
    >   apt install -y "php${PHP_VERSION}-sqlite3"
    > fi
    > 
    > #### WP-CLI
    > if ! command_exists wp; then
    >   log "Installing wp-cli..."
    >   curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
    &gt;   php wp-cli.phar --info &gt;/dev/null 2&gt;&amp;1 || true
    &gt;   chmod +x wp-cli.phar
    &gt;   mv wp-cli.phar /usr/local/bin/wp
    &gt; else
    &gt;   log "wp-cli present."
    &gt; fi
    &gt; 
    &gt; # -------------------------
    &gt; 
    &gt; ##### Database setup
    &gt; 
    &gt; # -------------------------
    &gt; 
    &gt; if [[ "$DB_ENGINE" == "mariadb" ]]; then
    &gt;   log "Creating database and user..."
    &gt;   mysql -e "CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
    &gt;   mysql -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';"
    >   mysql -e "GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'localhost';"
    >   mysql -e "FLUSH PRIVILEGES;"
    >   log "Database ${DB_NAME} and user ${DB_USER} created."
    > fi
    > 
    > # -------------------------
    > 
    > ##### Download and place ClassicPress
    > 
    > # -------------------------
    > 
    > log "Downloading ClassicPress..."
    > mkdir -p "$WEBROOT"
    > cd /tmp
    > CP_RELEASE_URL="https://github.com/ClassicPress/ClassicPress-release/archive/refs/tags/1.6.3.tar.gz"
    > wget -qO cp.tar.gz "$CP_RELEASE_URL"
    > tar -xzf cp.tar.gz
    > shopt -s dotglob
    > mv ClassicPress-release-*/* "$WEBROOT"/
    > rm -rf /tmp/ClassicPress-release-* cp.tar.gz
    > shopt -u dotglob
    > 
    > log "Setting ownership and permissions..."
    > chown -R www-data:www-data "$WEBROOT"
    > find "$WEBROOT" -type d -exec chmod 755 {} \;
    > find "$WEBROOT" -type f -exec chmod 644 {} \;
    > 
    > # -------------------------
    > 
    > ##### Caddy config
    > 
    > # -------------------------
    > 
    > log "Creating Caddyfile for ${DOMAIN}..."
    > cat > /etc/caddy/Caddyfile <<EOF
    > ${DOMAIN} {
    >     root * ${WEBROOT}
    >     encode gzip zstd
    >     php_fastcgi unix//run/php/php${PHP_VERSION}-fpm.sock
    >     file_server
    > }
    > EOF
    > 
    > log "Reloading Caddy..."
    > systemctl reload caddy || systemctl restart caddy
    > 
    > # -------------------------
    > 
    > ##### ClassicPress install via WP-CLI
    > 
    > # -------------------------
    > 
    > log "Preparing wp-config.php and running site install..."
    > # Create wp-config.php via wp-cli using DB engine chosen
    > if [[ "$DB_ENGINE" == "mariadb" ]]; then
    >   wp config create --path="$WEBROOT" --dbname="$DB_NAME" --dbuser="$DB_USER" --dbpass="$DB_PASS" --dbhost="localhost" --dbprefix="${DB_PREFIX}_" --skip-check --allow-root
    > else
    >   # use SQLite db.php drop-in approach
    >   # ensure php sqlite extension present
    >   WP_DB_DIR="$WEBROOT/wp-content/database"
    >   mkdir -p "$WP_DB_DIR"
    >   chown -R www-data:www-data "$WP_DB_DIR"
    >   # download db.php drop-in if available (wp-sqlite-db)
    >   wget -qO "$WEBROOT/wp-content/db.php" "https://raw.githubusercontent.com/aaemnnosttv/wp-sqlite-db/v1.3.2/src/db.php"
    >   wp config create --path="$WEBROOT" --skip-check --allow-root
    > fi
    > 
    > #### Install the site
    > wp core install --path="$WEBROOT" --url="https://${DOMAIN}" --title="ClassicPress Site" --admin_user="${ADMIN_USER}" --admin_password="${ADMIN_PASS}" --admin_email="${ADMIN_EMAIL}" --skip-email --allow-root
    &gt; 
    &gt; log "Installing default plugins/themes if requested..."
    &gt; if $INSTALL_PLUGINS; then
    &gt;   for pl in "${DEFAULT_PLUGINS[@]}"; do
    >     wp plugin install "$pl" --activate --path="$WEBROOT" --allow-root || warn "Failed to install plugin $pl"
    >   done
    > fi
    > for th in "${DEFAULT_THEMES[@]}"; do
    >   wp theme install "$th" --activate --path="$WEBROOT" --allow-root || warn "Failed to install theme $th"
    > done
    > 
    > # -------------------------
    > ##### SSL (Caddy auto handles TLS) and reminder
    > 
    > # -------------------------
    > 
    > log "Caddy will manage TLS automatically when DNS for ${DOMAIN} points to this server's IP."
    > log "If DNS is not yet pointed, TLS will be obtained automatically when it is."
    > 
    > # -------------------------
    > 
    > ##### Final summary
    > 
    > # -------------------------
    > 
    > echo
    > 
    > echo "===================================================="
    > 
    > echo " ClassicPress install complete (Caddy mode)"
    > echo " Domain: https://${DOMAIN}"
    > echo " Admin user: ${ADMIN_USER}"
    > echo " Admin password: ${ADMIN_PASS}"
    > if [[ "$DB_ENGINE" == "mariadb" ]]; then
    >   echo " DB: ${DB_NAME} / ${DB_USER} / ${DB_PASS}"
    > else
    >   echo " DB: SQLite (file stored in wp-content/database by default)"
    > fi
    > echo " Webroot: ${WEBROOT}"
    > echo " Caddyfile: /etc/caddy/Caddyfile"
    > echo " WP-CLI: /usr/local/bin/wp"
    > 
    > echo "===================================================="
    > 
    > 

    Note

    1. ** (or not - if you ask my wife)

    2. The usual questions came to mind- why not github repo/ Why not use pastebin, etc... before I posted the script here. Anyways, formatting in vanilla drove me nuts.

    3. Since much of the code was AI tested/ suggested, not sure about license either. Lets' call it a bored middle aged man project license.
      (gosh, am I really that old? OR MAybe.. use it ONLY if you are that old..lol)

    4. Sorry @bikegremlin, did not meant to hijack thread.

    Pretty cool - though it would make more sense to move it to a separate thread for easier finding in the future (and for any updates and feedback).

  • vyasvyas OGSenpai
    edited August 2025

    :-)

    On serious note, you may kindly move to cest pit. If we do separate thread, Accidentaly we might trigger a storm of AI bot “guided” scripts and make a mess out of les. Don’t want that now, do we ?

    Cheers

  • bikegremlinbikegremlin ModeratorOGContent Writer

    @vyas said:
    :-)

    On serious note, you may kindly move to cest pit. If we do separate thread, Accidentaly we might trigger a storm of AI bot “guided” scripts and make a mess out of les. Don’t want that now, do we ?

    Cheers

    IMO it would make more sense to move it to a normal theme under the WordPress section - and act differently if we do run into any problems.

  • @vyas said:
    :-)

    On serious note, you may kindly move to cest pit. If we do separate thread, Accidentaly we might trigger a storm of AI bot “guided” scripts and make a mess out of les. Don’t want that now, do we ?

    Cheers

    What? What's wrong with AI generated scripts (other then it not working)?

    I mean we can even have a AI generated script to post AI generated scripts to LES and have other AI generated scripts to reply and fix the AI generated scripts! Viva la AI generated scripts!

    I speak fluent sarcasm and broken logic. | I would agree with you, but thæn we’d both be wrong.

  • I've managed WordPress websites with templates and plugins I built myself, handling spikes of 2k concurrent users on $3/month VPSes.

    Later, another company hired me to do something similar for more than 10 million unique visitors per month, and it cost them around $2k/month including a larger server.

  • @imok said:

    I've managed WordPress websites with templates and plugins I built myself, handling spikes of 2k concurrent users on $3/month VPSes.

    Later, another company hired me to do something similar for more than 10 million unique visitors per month, and it cost them around $2k/month including a larger server.

    1998Euros profit - 2 euros infrastrcture

    I believe in good luck. Harder that I work ,luckier i get.

  • @Chievo said:

    @imok said:

    I've managed WordPress websites with templates and plugins I built myself, handling spikes of 2k concurrent users on $3/month VPSes.

    Later, another company hired me to do something similar for more than 10 million unique visitors per month, and it cost them around $2k/month including a larger server.

    1998Euros profit - 2 euros infrastrcture

    Actually I moved everything to bare metal to have peace of mind. I slept better after that.

  • @imok said:

    @Chievo said:

    @imok said:

    I've managed WordPress websites with templates and plugins I built myself, handling spikes of 2k concurrent users on $3/month VPSes.

    Later, another company hired me to do something similar for more than 10 million unique visitors per month, and it cost them around $2k/month including a larger server.

    1998Euros profit - 2 euros infrastrcture

    Actually I moved everything to bare metal to have peace of mind. I slept better after that.

    Baremetal never gives me piece of mind...

    A dedicated server running my own VMs on proxmos + onsite snapshot backup + offsite snapshot backup + offsite code backup is what gives me piece of mind. And yes, I know it's overkill, but this is what helps me sleep better at night so it's all worth it :D

    I speak fluent sarcasm and broken logic. | I would agree with you, but thæn we’d both be wrong.

  • Yes, that’s what I meant to say. I mixed up the words. I meant a dedicated server (plus a VPS for backups). I always run Proxmox on my dedis.

  • somiksomik OG
    edited August 2025

    @imok said:
    Yes, that’s what I meant to say. I mixed up the words. I meant a dedicated server (plus a VPS for backups). I always run Proxmox on my dedis.

    And a few idlers used to monitor uptime, right? :lol:

    Ya, proxmox is awesome. I dunno what to do if proxmox ever removes their community version...

    I speak fluent sarcasm and broken logic. | I would agree with you, but thæn we’d both be wrong.

  • @somik said: And a few idlers used to monitor uptime, right?

    I use Hetrixtools (free plan), otherwise I would need another server to monitor my monitoring server and so on...

  • @bikegremlin oh I gots problems bud BIG problems!!!!

    The Yeti has left the building.

  • bikegremlinbikegremlin ModeratorOGContent Writer

    @AuroraZero said:
    @bikegremlin oh I gots problems bud BIG problems!!!!

    Should I send Rocky and Knuckles over?

  • @bikegremlin said:

    @AuroraZero said:
    @bikegremlin oh I gots problems bud BIG problems!!!!

    Should I send Rocky and Knuckles over?

    Not unless they have a j2534 passthru and a GM SPS subscription lol

    The Yeti has left the building.

  • bikegremlinbikegremlin ModeratorOGContent Writer

    @AuroraZero said:

    @bikegremlin said:

    @AuroraZero said:
    @bikegremlin oh I gots problems bud BIG problems!!!!

    Should I send Rocky and Knuckles over?

    Not unless they have a j2534 passthru and a GM SPS subscription lol

    We got SPS - since the very start of the 90s! :)

  • @bikegremlin said:

    @AuroraZero said:

    @bikegremlin said:

    @AuroraZero said:
    @bikegremlin oh I gots problems bud BIG problems!!!!

    Should I send Rocky and Knuckles over?

    Not unless they have a j2534 passthru and a GM SPS subscription lol

    We got SPS - since the very start of the 90s! :)

    I don't know what you have confused SPS for but it is not a VD bud!!!

    The Yeti has left the building.

Sign In or Register to comment.