Created
May 26, 2025 21:19
-
-
Save lemassykoi/73f407f7278fc15dd9febff9a5351281 to your computer and use it in GitHub Desktop.
ESPOCRM 9.1.3 Install script without docker, on a fresh Debian 12 VM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# install_espocrm.sh - Automated, production-ready EspoCRM installer for Debian 12 (without Docker) | |
# Inspired by EspoCRM official installer | |
set -euo pipefail | |
# Default configuration | |
DOMAIN="espocrm.mydomain.local" | |
ADMIN_EMAIL="admin@${DOMAIN}" | |
ESPO_VERSION="9.1.3" | |
INSTALL_DIR="/var/www/espocrm" | |
DB_NAME="espocrm" | |
DB_USER="espocrm_user" | |
DB_PASS="" | |
WWW_USER="www-data" | |
WWW_GROUP="www-data" | |
function usage() { | |
cat <<EOF | |
Usage: $0 [-d domain] [-e admin_email] [-v version] [-i install_dir] \ | |
[-n db_name] [-u db_user] [-p db_pass] [-w web_user] | |
Options: | |
-d DOMAIN EspoCRM domain (ServerName) [default: $DOMAIN] | |
-e ADMIN_EMAIL Admin email for notifications [default: $ADMIN_EMAIL] | |
-v VERSION EspoCRM version [default: $ESPO_VERSION] | |
-i INSTALL_DIR Installation directory [default: $INSTALL_DIR] | |
-n DB_NAME Database name [default: $DB_NAME] | |
-u DB_USER Database user [default: $DB_USER] | |
-p DB_PASS Database password (random if omitted) | |
-w WEB_USER Webserver user (owner) [default: $WWW_USER] | |
-h Show this help message | |
EOF | |
exit 1 | |
} | |
# Parse CLI arguments | |
while getopts d:e:v:i:n:u:p:w:h flag; do | |
case "${flag}" in | |
d) DOMAIN="${OPTARG}";; | |
e) ADMIN_EMAIL="${OPTARG}";; | |
v) ESPO_VERSION="${OPTARG}";; | |
i) INSTALL_DIR="${OPTARG}";; | |
n) DB_NAME="${OPTARG}";; | |
u) DB_USER="${OPTARG}";; | |
p) DB_PASS="${OPTARG}";; | |
w) WWW_USER="${OPTARG}"; WWW_GROUP="$WWW_USER";; | |
*) usage;; | |
esac | |
done | |
# Generate random DB password if not provided | |
if [ -z "$DB_PASS" ]; then | |
DB_PASS=$(openssl rand -base64 16) | |
fi | |
# Ensure script runs as root | |
if [ "$EUID" -ne 0 ]; then | |
echo "Please run as root or with sudo." | |
exit 1 | |
fi | |
# Logging helpers | |
log() { echo -e "[\e[32mINFO\e[0m] $*"; } | |
error() { echo -e "[\e[31mERROR\e[0m] $*"; exit 1; } | |
log "Starting EspoCRM ${ESPO_VERSION} installation" | |
### PART 1: SYSTEM UPDATE & DEPENDENCIES ### | |
log "Updating apt and installing packages..." | |
apt update | |
apt install -y \ | |
apache2 php-fpm php-cli php-mysql php-json php-gd php-zip php-imap \ | |
php-mbstring php-curl php-exif php-ldap php-xml php-bcmath unzip wget openssl mariadb-server | |
### PART 2: PHP MODULES & CONFIGURATION ### | |
log "Enabling PHP modules and restarting service..." | |
phpenmod imap mbstring | |
PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') | |
systemctl restart "php${PHP_VERSION}-fpm" | |
log "Configuring php.ini..." | |
for MODE in fpm cli; do | |
INI_FILE="/etc/php/${PHP_VERSION}/${MODE}/php.ini" | |
cp -n "$INI_FILE" "${INI_FILE}.orig" | |
sed -i \ | |
-e "s/^;\?max_execution_time\s*=.*/max_execution_time = 180/" \ | |
-e "s/^;\?max_input_time\s*=.*/max_input_time = 180/" \ | |
-e "s/^;\?memory_limit\s*=.*/memory_limit = 256M/" \ | |
-e "s/^;\?post_max_size\s*=.*/post_max_size = 50M/" \ | |
-e "s/^;\?upload_max_filesize\s*=.*/upload_max_filesize = 50M/" \ | |
"$INI_FILE" | |
done | |
### PART 3: APACHE CONFIGURATION ### | |
log "Configuring Apache virtual host..." | |
a2enmod rewrite headers expires alias proxy_fcgi setenvif | |
# Disable default site | |
a2dissite 000-default.conf | |
VHOST_CONF="/etc/apache2/sites-available/espocrm.conf" | |
cat <<EOF > "$VHOST_CONF" | |
<VirtualHost *:80> | |
ServerName ${DOMAIN} | |
ServerAdmin ${ADMIN_EMAIL} | |
DocumentRoot ${INSTALL_DIR}/public | |
Alias /client/ ${INSTALL_DIR}/client/ | |
<Directory /var/www/html> | |
AllowOverride None | |
Require all granted | |
</Directory> | |
<Directory ${INSTALL_DIR}/public> | |
AllowOverride All | |
Require all granted | |
</Directory> | |
DirectoryIndex index.php index.html | |
<FilesMatch "\.php$"> | |
SetHandler "proxy:unix:/run/php/php${PHP_VERSION}-fpm.sock|fcgi://localhost/" | |
</FilesMatch> | |
<IfModule mod_expires.c> | |
ExpiresActive On | |
ExpiresByType image/* "access plus 1 month" | |
ExpiresByType text/css "access plus 1 week" | |
ExpiresByType application/javascript "access plus 1 week" | |
</IfModule> | |
ErrorLog /var/log/apache2/espocrm_error.log | |
CustomLog /var/log/apache2/espocrm_access.log combined | |
</VirtualHost> | |
EOF | |
a2ensite espocrm.conf | |
systemctl reload apache2 | |
### PART 4: DATABASE SETUP ### | |
log "Creating MariaDB database and user..." | |
mysql -e "CREATE DATABASE IF NOT EXISTS $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" | |
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}, User: ${DB_USER}, Pass: ${DB_PASS}" | |
### PART 5: EspoCRM DOWNLOAD & EXTRACT ### | |
log "Downloading EspoCRM ${ESPO_VERSION}..." | |
ZIP_FILE="/tmp/espocrm-${ESPO_VERSION}.zip" | |
wget -q -O "$ZIP_FILE" "https://www.espocrm.com/downloads/EspoCRM-${ESPO_VERSION}.zip" | |
log "Extracting to ${INSTALL_DIR}..." | |
# Unpack into a temporary directory to handle nested folder structure | |
TMP_DIR=$(mktemp -d) | |
unzip -q "$ZIP_FILE" -d "$TMP_DIR" | |
# Move contents of the extracted folder into INSTALL_DIR | |
rm -rf "$INSTALL_DIR" | |
mkdir -p "$INSTALL_DIR" | |
EXTRACTED_SUBDIR=$(find "$TMP_DIR" -maxdepth 1 -type d ! -path "$TMP_DIR" | head -n1) | |
if [ -d "$EXTRACTED_SUBDIR" ]; then | |
mv "$EXTRACTED_SUBDIR"/* "$INSTALL_DIR"/ | |
else | |
mv "$TMP_DIR"/* "$INSTALL_DIR"/ | |
fi | |
# Clean up | |
rm -rf "$TMP_DIR" | |
rm -f "$ZIP_FILE" | |
### PART 6: PERMISSIONS & OWNERSHIP ### | |
log "Setting permissions and ownership..." | |
cd "$INSTALL_DIR" | |
# Default permissions | |
find . -type d -exec chmod 755 {} + | |
find . -type f -exec chmod 644 {} + | |
# Writable directories if they exist | |
for d in data custom client/custom application/Espo/Modules client/modules; do | |
if [ -d "$d" ]; then | |
find "$d" -type d -exec chmod 775 {} + | |
find "$d" -type f -exec chmod 664 {} + | |
fi | |
done | |
# Executable | |
if [ -f bin/command ]; then | |
chmod 754 bin/command | |
fi | |
# Ownership | |
chown -R ${WWW_USER}:${WWW_GROUP} . | |
### PART 7: CRON JOB ### | |
log "Installing cron job for EspoCRM..." | |
CRON_TAB="* * * * * /usr/bin/php -f ${INSTALL_DIR}/cron.php > /dev/null 2>&1" | |
# Safely append cron job for WWW_USER, preserving existing entries | |
tmp_cron=$(mktemp) | |
if crontab -u ${WWW_USER} -l > "$tmp_cron" 2>/dev/null; then | |
grep -Fv "${INSTALL_DIR}/cron.php" "$tmp_cron" > "${tmp_cron}.filtered" | |
else | |
touch "${tmp_cron}.filtered" | |
fi | |
printf "%s | |
" "$CRON_TAB" >> "${tmp_cron}.filtered" | |
crontab -u ${WWW_USER} "${tmp_cron}.filtered" | |
rm -f "$tmp_cron" "${tmp_cron}.filtered" | |
log "EspoCRM ${ESPO_VERSION} installation complete!" | |
log "!! Take note of SQL User password !!" | |
log "REBOOT then Visit http://${DOMAIN} to finish setup." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment