Docker-based self-hosted WordPress deployment system with: - Four-container stack (nginx, wordpress/php-fpm, mariadb, certbot) - Automatic SSL via Let's Encrypt with self-signed fallback - First-boot WordPress setup via WP-CLI (GeneratePress + child theme, plugins) - Interactive setup wizard and one-line install script - Backup, update, healthcheck, and SSL renewal scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
182 lines
6.7 KiB
Bash
Executable File
182 lines
6.7 KiB
Bash
Executable File
#!/bin/bash
|
|
set -eo pipefail
|
|
|
|
MARKER_COMPLETE="/var/www/html/.websitebox-setup-complete"
|
|
MARKER_PARTIAL="/var/www/html/.websitebox-setup-partial"
|
|
|
|
# Run the default WordPress docker-entrypoint.sh first
|
|
# This sets up wp-config.php, copies WordPress files, then execs php-fpm
|
|
docker-entrypoint.sh php-fpm &
|
|
WP_PID=$!
|
|
|
|
# Forward signals to PHP-FPM for graceful shutdown
|
|
trap 'kill -TERM $WP_PID; wait $WP_PID; exit $?' TERM INT
|
|
|
|
# Wait for WordPress files AND wp-config.php to be available
|
|
echo "WebsiteBox: Waiting for WordPress files..."
|
|
for i in $(seq 1 60); do
|
|
if [ -f /var/www/html/wp-includes/version.php ] && [ -f /var/www/html/wp-config.php ]; then
|
|
echo "WebsiteBox: WordPress files ready."
|
|
break
|
|
fi
|
|
if [ "$i" -eq 60 ]; then
|
|
echo "ERROR: WordPress files did not appear after 120 seconds."
|
|
exit 1
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
# Copy mu-plugins into place
|
|
mkdir -p /var/www/html/wp-content/mu-plugins
|
|
cp -f /usr/src/websitebox-mu-plugins/*.php /var/www/html/wp-content/mu-plugins/ 2>/dev/null || true
|
|
chown -R www-data:www-data /var/www/html/wp-content/mu-plugins/
|
|
|
|
# Check if setup is already complete
|
|
if [ -f "${MARKER_COMPLETE}" ]; then
|
|
echo "WebsiteBox: Setup already complete. Starting normally."
|
|
wait $WP_PID
|
|
exit $?
|
|
fi
|
|
|
|
echo "WebsiteBox: Running first-boot setup..."
|
|
|
|
# If partial marker exists, we're retrying
|
|
if [ -f "${MARKER_PARTIAL}" ]; then
|
|
echo "WebsiteBox: Previous setup was partial. Re-running setup idempotently..."
|
|
fi
|
|
|
|
# Wait for MariaDB to be ready
|
|
echo "WebsiteBox: Waiting for database..."
|
|
DB_READY=false
|
|
for i in $(seq 1 30); do
|
|
if su -s /bin/sh -c 'wp db check --path=/var/www/html 2>/dev/null' www-data; then
|
|
DB_READY=true
|
|
echo "WebsiteBox: Database is ready."
|
|
break
|
|
fi
|
|
echo "WebsiteBox: Database not ready, attempt ${i}/30..."
|
|
sleep 2
|
|
done
|
|
|
|
if [ "${DB_READY}" != "true" ]; then
|
|
echo "ERROR: WebsiteBox could not connect to database after 30 attempts."
|
|
echo "Check that the db container is running: docker compose ps db"
|
|
exit 1
|
|
fi
|
|
|
|
# Track if any step fails
|
|
SETUP_FAILED=false
|
|
|
|
# Install WordPress core if not already installed
|
|
# Using env vars directly (inherited by su without -l) avoids shell quoting issues
|
|
echo "WebsiteBox: Installing WordPress core..."
|
|
if ! su -s /bin/sh -c 'wp core is-installed --path=/var/www/html 2>/dev/null' www-data; then
|
|
# shellcheck disable=SC2016
|
|
# Single quotes intentional: inner shell (via su) expands env vars
|
|
if ! su -s /bin/sh www-data -c 'wp core install \
|
|
--path=/var/www/html \
|
|
--url="https://${DOMAIN}" \
|
|
--title="${SITE_TITLE:-My Portfolio}" \
|
|
--admin_user="${ADMIN_USER}" \
|
|
--admin_password="${ADMIN_PASSWORD}" \
|
|
--admin_email="${ADMIN_EMAIL}" \
|
|
--skip-email'; then
|
|
echo "ERROR: WordPress core install failed."
|
|
SETUP_FAILED=true
|
|
fi
|
|
else
|
|
echo "WebsiteBox: WordPress core already installed."
|
|
fi
|
|
|
|
# Install and activate GeneratePress theme
|
|
if [ "${SETUP_FAILED}" != "true" ]; then
|
|
echo "WebsiteBox: Installing GeneratePress theme..."
|
|
su -s /bin/sh -c 'wp theme install generatepress --path=/var/www/html 2>/dev/null' www-data || true
|
|
fi
|
|
|
|
# Copy and activate child theme
|
|
echo "WebsiteBox: Installing WebsiteBox child theme..."
|
|
cp -r /usr/src/websitebox-theme/ /var/www/html/wp-content/themes/websitebox/
|
|
chown -R www-data:www-data /var/www/html/wp-content/themes/websitebox/
|
|
|
|
if [ "${SETUP_FAILED}" != "true" ]; then
|
|
su -s /bin/sh -c 'wp theme activate websitebox --path=/var/www/html' www-data || {
|
|
echo "WARNING: Could not activate child theme. Activating GeneratePress instead."
|
|
su -s /bin/sh -c 'wp theme activate generatepress --path=/var/www/html' www-data || true
|
|
}
|
|
fi
|
|
|
|
# Install and activate plugins
|
|
if [ "${SETUP_FAILED}" != "true" ]; then
|
|
echo "WebsiteBox: Installing plugins..."
|
|
|
|
for plugin in age-gate wordfence updraftplus; do
|
|
echo "WebsiteBox: Installing ${plugin}..."
|
|
if ! su -s /bin/sh -c "wp plugin install ${plugin} --activate --path=/var/www/html 2>/dev/null" www-data; then
|
|
echo "WARNING: Failed to install ${plugin}. Will continue with remaining setup."
|
|
SETUP_FAILED=true
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Configure WordPress settings
|
|
if [ "${SETUP_FAILED}" != "true" ]; then
|
|
echo "WebsiteBox: Configuring WordPress settings..."
|
|
|
|
# Set permalink structure
|
|
su -s /bin/sh -c "wp rewrite structure '/%postname%/' --path=/var/www/html" www-data || true
|
|
|
|
# Delete default content
|
|
su -s /bin/sh -c 'wp post delete 1 --force --path=/var/www/html 2>/dev/null' www-data || true
|
|
su -s /bin/sh -c 'wp post delete 2 --force --path=/var/www/html 2>/dev/null' www-data || true
|
|
|
|
# Set timezone
|
|
su -s /bin/sh -c "wp option update timezone_string 'UTC' --path=/var/www/html" www-data || true
|
|
|
|
# Discourage search engines until user is ready
|
|
su -s /bin/sh -c "wp option update blog_public 0 --path=/var/www/html" www-data || true
|
|
fi
|
|
|
|
# Configure Age Gate
|
|
if [ "${SETUP_FAILED}" != "true" ] && [ "${AGE_GATE_ENABLED}" = "true" ]; then
|
|
echo "WebsiteBox: Configuring Age Gate..."
|
|
# shellcheck disable=SC2016
|
|
su -s /bin/sh -c 'wp option update age_gate_min_age "${AGE_GATE_MIN_AGE:-18}" --path=/var/www/html' www-data || true
|
|
su -s /bin/sh -c 'wp option update age_gate_restrict_all "1" --path=/var/www/html' www-data || true
|
|
fi
|
|
|
|
# Disable Age Gate if not enabled
|
|
if [ "${AGE_GATE_ENABLED}" != "true" ]; then
|
|
echo "WebsiteBox: Age Gate disabled. Deactivating plugin..."
|
|
su -s /bin/sh -c 'wp plugin deactivate age-gate --path=/var/www/html 2>/dev/null' www-data || true
|
|
fi
|
|
|
|
# Configure UpdraftPlus backup path
|
|
echo "WebsiteBox: Configuring UpdraftPlus backup path..."
|
|
su -s /bin/sh -c "wp option update updraft_dir '/var/backups/websitebox' --path=/var/www/html" www-data || true
|
|
|
|
# Set backup retention
|
|
RETENTION="${BACKUP_RETENTION_DAYS:-30}"
|
|
DB_RETAIN=$((RETENTION > 30 ? 30 : RETENTION))
|
|
FILE_RETAIN=$((RETENTION / 7))
|
|
[ "${FILE_RETAIN}" -lt 1 ] && FILE_RETAIN=1
|
|
su -s /bin/sh -c "wp option update updraft_retain '${FILE_RETAIN}' --path=/var/www/html" www-data || true
|
|
su -s /bin/sh -c "wp option update updraft_retain_db '${DB_RETAIN}' --path=/var/www/html" www-data || true
|
|
|
|
# Create marker file
|
|
if [ "${SETUP_FAILED}" = "true" ]; then
|
|
touch "${MARKER_PARTIAL}"
|
|
chown www-data:www-data "${MARKER_PARTIAL}"
|
|
echo "WARNING: WebsiteBox setup completed with errors."
|
|
echo "Some plugins/themes may not have installed correctly."
|
|
echo "To retry: docker compose restart wordpress"
|
|
else
|
|
rm -f "${MARKER_PARTIAL}"
|
|
touch "${MARKER_COMPLETE}"
|
|
chown www-data:www-data "${MARKER_COMPLETE}"
|
|
echo "WebsiteBox: First-run setup complete!"
|
|
fi
|
|
|
|
# Wait for the background PHP-FPM process
|
|
wait $WP_PID
|