Last active
February 16, 2025 04:37
-
-
Save cballenar/70e2bf812ae565a1388460b01e468756 to your computer and use it in GitHub Desktop.
LEMP Provisioning script based on Laravel Forge. Generated on 2020-04-26.
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
# LEMP Provisioning Script | |
# - Script generated by Laravel Forge on 20200426 | |
# - Built for Ubuntu 18.04LTS, PHP7.2, MySQL5.7 | |
# Variables | |
#------------------------------------------------------------------------------- | |
my_hostname="" # mydomain | |
my_username="" # myuser | |
my_root_pw="" # **************** | |
my_sshkey="" # ssh-rsa AAAA... | |
my_db_pw="" # **************** | |
my_ip_address="" # 127.0.0.1 (for mysql use) | |
my_git_name="" # My Full Name | |
my_git_email="" # [email protected] | |
event_id="" # 000001 | |
server_id="" # 100001 | |
recipe_id="" # 200001 | |
callback_url="" # https://myapp.com/provisioning/callback/app | |
server_ram=1024 # 1024 | |
php_memory_limit=$((server_ram / 2)) # wild guess | |
# Configurations | |
#------------------------------------------------------------------------------- | |
set_hostname=false | |
install_ufw=false | |
install_mysql=false | |
#------------------------------------------------------------------------------- | |
# REQUIRES: | |
# - sudo_password (random password for sudo) | |
# - db_password (random password for database user) | |
# | |
# Prefer IPv4 over IPv6 - make apt-get faster (?) | |
# src: https://laracasts.com/discuss/channels/forge/the-meaning-of-this-line-in-forgesh | |
sudo sed -i "s/#precedence ::ffff:0:0\/96 100/precedence ::ffff:0:0\/96 100/" /etc/gai.conf | |
# Use default answers and make script non interactive | |
export DEBIAN_FRONTEND=noninteractive | |
# Upgrade The Base Packages | |
apt-get update | |
apt-get upgrade -y | |
# Add A Few PPAs To Stay Current | |
apt-get install -y --force-yes software-properties-common | |
# apt-add-repository ppa:fkrull/deadsnakes-python2.7 -y | |
apt-add-repository ppa:nginx/mainline -y | |
apt-add-repository ppa:chris-lea/redis-server -y | |
apt-add-repository ppa:ondrej/apache2 -y | |
apt-add-repository ppa:ondrej/php -y | |
# Update Package Lists | |
apt-get update | |
# Base Packages | |
add-apt-repository universe | |
apt-get install -y --force-yes build-essential curl pkg-config fail2ban gcc git libmcrypt4 libpcre3-dev \ | |
make python2.7 python-pip sendmail supervisor unattended-upgrades unzip whois zsh ncdu awscli uuid-runtime | |
# Install Python Httpie | |
pip install httpie | |
# Disable Password Authentication Over SSH | |
sed -i "/PasswordAuthentication yes/d" /etc/ssh/sshd_config | |
echo "" | sudo tee -a /etc/ssh/sshd_config | |
echo "" | sudo tee -a /etc/ssh/sshd_config | |
echo "PasswordAuthentication no" | sudo tee -a /etc/ssh/sshd_config | |
# Restart SSH | |
ssh-keygen -A | |
service ssh restart | |
# Set The Hostname If Necessary | |
if $set_hostname ; then | |
echo "$my_hostname" > /etc/hostname | |
sed -i 's/127\.0\.0\.1.*localhost/127.0.0.1 $my_hostname.localdomain $my_hostname localhost/' /etc/hosts | |
hostname "$my_hostname" | |
fi | |
# Set The Timezone | |
ln -sf /usr/share/zoneinfo/UTC /etc/localtime | |
# Create The Root SSH Directory If Necessary | |
echo "Configure Users" | |
if [ ! -d /root/.ssh ] | |
then | |
mkdir -p /root/.ssh | |
touch /root/.ssh/authorized_keys | |
fi | |
# Setup Custom User | |
useradd "$my_username" | |
mkdir -p /home/"$my_username"/.ssh | |
mkdir -p /home/"$my_username"/.forge | |
adduser "$my_username" sudo | |
# Setup Bash For Custom User | |
chsh -s /bin/bash "$my_username" | |
cp /root/.profile /home/"$my_username"/.profile | |
cp /root/.bashrc /home/"$my_username"/.bashrc | |
# Set The Sudo Password For Forge | |
# Passes encrypted password so it can be taken in the script | |
PASSWORD="$(mkpasswd "$my_root_password")" | |
usermod --password $PASSWORD "$my_username" | |
# Build Formatted Keys & Copy Keys To Forge | |
cat > /root/.ssh/authorized_keys << EOF | |
# Laravel Forge | |
$my_sshkey | |
EOF | |
cp /root/.ssh/authorized_keys /home/"$my_username"/.ssh/authorized_keys | |
# Create The Server SSH Key | |
echo "Create the server SSH Key" | |
ssh-keygen -f /home/"$my_username"/.ssh/id_rsa -t rsa -N "" | |
# Copy Source Control Public Keys Into Known Hosts File | |
ssh-keyscan -H github.com >> /home/"$my_username"/.ssh/known_hosts | |
ssh-keyscan -H bitbucket.org >> /home/"$my_username"/.ssh/known_hosts | |
ssh-keyscan -H gitlab.com >> /home/"$my_username"/.ssh/known_hosts | |
# Configure Git Settings | |
git config --global user.name $my_git_name | |
git config --global user.email $my_git_email | |
# Add The Reconnect Script Into Forge Directory | |
echo "Add The Reconnect Script Into Forge Directory" | |
cat > /home/"$my_username"/.forge/reconnect << EOF | |
#!/usr/bin/env bash | |
echo "# Laravel Forge" | tee -a /home/$my_username/.ssh/authorized_keys > /dev/null | |
echo \$1 | tee -a /home/$my_username/.ssh/authorized_keys > /dev/null | |
echo "# Laravel Forge" | tee -a /root/.ssh/authorized_keys > /dev/null | |
echo \$1 | tee -a /root/.ssh/authorized_keys > /dev/null | |
echo "Keys Added!" | |
EOF | |
# Setup Forge Home Directory Permissions | |
chown -R "$my_username":"$my_username" /home/"$my_username" | |
chmod -R 755 /home/"$my_username" | |
chmod 700 /home/"$my_username"/.ssh/id_rsa | |
# Setup UFW Firewall | |
if $install_ufw ; then | |
echo 'Installing ufw' | |
apt-get install -y --force-yes ufw | |
ufw allow 22 | |
ufw allow 80 | |
ufw allow 443 | |
ufw --force enable | |
fi | |
# Allow FPM Restart | |
echo "$my_username ALL=NOPASSWD: /usr/sbin/service php7.4-fpm reload" > /etc/sudoers.d/php-fpm | |
echo "$my_username ALL=NOPASSWD: /usr/sbin/service php7.3-fpm reload" >> /etc/sudoers.d/php-fpm | |
echo "$my_username ALL=NOPASSWD: /usr/sbin/service php7.2-fpm reload" >> /etc/sudoers.d/php-fpm | |
echo "$my_username ALL=NOPASSWD: /usr/sbin/service php7.1-fpm reload" >> /etc/sudoers.d/php-fpm | |
echo "$my_username ALL=NOPASSWD: /usr/sbin/service php7.0-fpm reload" >> /etc/sudoers.d/php-fpm | |
echo "$my_username ALL=NOPASSWD: /usr/sbin/service php5.6-fpm reload" >> /etc/sudoers.d/php-fpm | |
echo "$my_username ALL=NOPASSWD: /usr/sbin/service php5-fpm reload" >> /etc/sudoers.d/php-fpm | |
# Install Base PHP Packages | |
apt-get install -y --force-yes php7.2-cli php7.2-dev \ | |
php7.2-pgsql php7.2-sqlite3 php7.2-gd \ | |
php7.2-curl php7.2-memcached \ | |
php7.2-imap php7.2-mysql php7.2-mbstring \ | |
php7.2-xml php7.2-zip php7.2-bcmath php7.2-soap \ | |
php7.2-intl php7.2-readline | |
# Install Composer Package Manager | |
curl -sS https://getcomposer.org/installer | php | |
mv composer.phar /usr/local/bin/composer | |
# Misc. PHP CLI Configuration | |
sudo sed -i "s/error_reporting = .*/error_reporting = E_ALL/" /etc/php/7.2/cli/php.ini | |
sudo sed -i "s/display_errors = .*/display_errors = On/" /etc/php/7.2/cli/php.ini | |
sudo sed -i "s/memory_limit = .*/memory_limit = ${php_memory_limit}M/" /etc/php/7.2/cli/php.ini | |
sudo sed -i "s/;date.timezone.*/date.timezone = UTC/" /etc/php/7.2/cli/php.ini | |
# Configure Sessions Directory Permissions | |
chmod 733 /var/lib/php/sessions | |
chmod +t /var/lib/php/sessions | |
#------------------------------------------------------------------------------- | |
# REQUIRES: | |
# - server (the forge server instance) | |
# - site_name (the name of the site folder) | |
# | |
# Install Nginx & PHP-FPM | |
echo "install Nginx" | |
apt-get install -y --force-yes nginx php7.2-fpm | |
systemctl enable nginx.service | |
# Generate dhparam File (?) | |
openssl dhparam -out /etc/nginx/dhparams.pem 2048 | |
# Tweak Some PHP-FPM Settings | |
sed -i "s/error_reporting = .*/error_reporting = E_ALL/" /etc/php/7.2/fpm/php.ini | |
sed -i "s/display_errors = .*/display_errors = On/" /etc/php/7.2/fpm/php.ini | |
sed -i "s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/" /etc/php/7.2/fpm/php.ini | |
sed -i "s/memory_limit = .*/memory_limit = ${php_memory_limit}M/" /etc/php/7.2/fpm/php.ini | |
sed -i "s/;date.timezone.*/date.timezone = UTC/" /etc/php/7.2/fpm/php.ini | |
# Configure FPM Pool Settings | |
sed -i "s/^user = www-data/user = $my_username/" /etc/php/7.2/fpm/pool.d/www.conf | |
sed -i "s/^group = www-data/group = $my_username/" /etc/php/7.2/fpm/pool.d/www.conf | |
sed -i "s/;listen\.owner.*/listen.owner = $my_username/" /etc/php/7.2/fpm/pool.d/www.conf | |
sed -i "s/;listen\.group.*/listen.group = $my_username/" /etc/php/7.2/fpm/pool.d/www.conf | |
sed -i "s/;listen\.mode.*/listen.mode = 0666/" /etc/php/7.2/fpm/pool.d/www.conf | |
sed -i "s/;request_terminate_timeout.*/request_terminate_timeout = 60/" /etc/php/7.2/fpm/pool.d/www.conf | |
# Configure Primary Nginx Settings | |
sed -i "s/user www-data;/user $my_username;/" /etc/nginx/nginx.conf | |
sed -i "s/worker_processes.*/worker_processes auto;/" /etc/nginx/nginx.conf | |
sed -i "s/# multi_accept.*/multi_accept on;/" /etc/nginx/nginx.conf | |
sed -i "s/# server_names_hash_bucket_size.*/server_names_hash_bucket_size 128;/" /etc/nginx/nginx.conf | |
# Configure Gzip | |
cat > /etc/nginx/conf.d/gzip.conf << EOF | |
gzip_comp_level 5; | |
gzip_min_length 256; | |
gzip_proxied any; | |
gzip_vary on; | |
gzip_types | |
application/atom+xml | |
application/javascript | |
application/json | |
application/rss+xml | |
application/vnd.ms-fontobject | |
application/x-font-ttf | |
application/x-web-app-manifest+json | |
application/xhtml+xml | |
application/xml | |
font/opentype | |
image/svg+xml | |
image/x-icon | |
text/css | |
text/plain | |
text/x-component; | |
EOF | |
# Disable The Default Nginx Site | |
rm /etc/nginx/sites-enabled/default | |
rm /etc/nginx/sites-available/default | |
service nginx restart | |
# Install A Catch All Server | |
cat > /etc/nginx/sites-available/catch-all << EOF | |
server { | |
return 404; | |
} | |
EOF | |
ln -s /etc/nginx/sites-available/catch-all /etc/nginx/sites-enabled/catch-all | |
# Restart Nginx & PHP-FPM Services | |
service nginx reload | |
if [ ! -z "\$(ps aux | grep php-fpm | grep -v grep)" ] | |
then | |
service php7.4-fpm restart > /dev/null 2>&1 | |
service php7.3-fpm restart > /dev/null 2>&1 | |
service php7.2-fpm restart > /dev/null 2>&1 | |
service php7.1-fpm restart > /dev/null 2>&1 | |
service php7.0-fpm restart > /dev/null 2>&1 | |
service php5.6-fpm restart > /dev/null 2>&1 | |
service php5-fpm restart > /dev/null 2>&1 | |
fi | |
# Add Custom User To www-data Group | |
usermod -a -G www-data "$my_username" | |
id "$my_username" | |
groups "$my_username" | |
# Install Node | |
curl --silent --location https://deb.nodesource.com/setup_12.x | bash - | |
# Update | |
apt-get update | |
# Install Node.js | |
sudo apt-get install -y --force-yes nodejs | |
# Install package managers | |
npm install -g pm2 | |
npm install -g gulp | |
npm install -g yarn | |
#------------------------------------------------------------------------------- | |
# REQUIRES: | |
# - server (the forge server instance) | |
# - db_password (random password for mysql user) | |
# | |
if $install_mysql ; then | |
# Set The Automated Root Password | |
export DEBIAN_FRONTEND=noninteractive | |
echo "Installing MySQL" | |
debconf-set-selections <<< "mysql-community-server mysql-community-server/data-dir select ''" | |
debconf-set-selections <<< "mysql-community-server mysql-community-server/root-pass password $my_db_pw" | |
debconf-set-selections <<< "mysql-community-server mysql-community-server/re-root-pass password $my_db_pw" | |
# Install MySQL | |
apt-get install -y mysql-server | |
# Configure Password Expiration | |
echo "default_password_lifetime = 0" >> /etc/mysql/mysql.conf.d/mysqld.cnf | |
# # Configure Max Connections | |
# # What is this doing? | |
# # Is 1024 based on the RAM? (1024 was the value entered as RAM available) | |
# RAM=$(awk '/^MemTotal:/{printf "%3.0f", $2 / (1024 * 1024)}' /proc/meminfo) | |
# # Multiple ram available by 70. (Why?) | |
# MAX_CONNECTIONS=$(( 70 * $RAM )) | |
# # if max_connections > 70; then use max_connections; else use 100 | |
# REAL_MAX_CONNECTIONS=$(( MAX_CONNECTIONS>70 ? MAX_CONNECTIONS : 100 )) | |
# # set real_max_connections in my.cnf | |
# sed -i "s/^max_connections.*=.*/max_connections=${REAL_MAX_CONNECTIONS}/" /etc/mysql/my.cnf | |
# # this is actually not working as my.cnf doesn't have that text to replace | |
# Configure Access Permissions For Root & Custom Users | |
sed -i '/^bind-address/s/bind-address.*=.*/bind-address = */' /etc/mysql/mysql.conf.d/mysqld.cnf | |
mysql --user="root" --password="$my_db_pw" -e "GRANT ALL ON *.* TO root@'$my_db_address' IDENTIFIED BY '$my_db_pw';" | |
mysql --user="root" --password="$my_db_pw" -e "GRANT ALL ON *.* TO root@'%' IDENTIFIED BY '$my_db_pw';" | |
service mysql restart | |
mysql --user="root" --password="$my_db_pw" -e "CREATE USER '$my_username'@'$my_db_address' IDENTIFIED BY '$my_db_pw';" | |
mysql --user="root" --password="$my_db_pw" -e "GRANT ALL ON *.* TO '$my_username'@'$my_db_address' IDENTIFIED BY '$my_db_pw' WITH GRANT OPTION;" | |
mysql --user="root" --password="$my_db_pw" -e "GRANT ALL ON *.* TO '$my_username'@'%' IDENTIFIED BY '$my_db_pw' WITH GRANT OPTION;" | |
mysql --user="root" --password="$my_db_pw" -e "FLUSH PRIVILEGES;" | |
# Create The Initial Database If Specified | |
mysql --user="root" --password="$my_db_pw" -e "CREATE DATABASE $my_username CHARACTER SET utf8 COLLATE utf8_unicode_ci;" | |
fi | |
# Install & Configure Redis Server | |
apt-get install -y redis-server | |
sed -i 's/bind 127.0.0.1/bind 0.0.0.0/' /etc/redis/redis.conf | |
service redis-server restart | |
systemctl enable redis-server | |
yes '' | pecl install -f redis | |
# Ensure PHPRedis extension is available | |
if pecl list | grep redis >/dev/null 2>&1; | |
then | |
echo "Configuring PHPRedis" | |
echo "extension=redis.so" > /etc/php/7.2/mods-available/redis.ini | |
yes '' | apt install php-redis | |
fi | |
# Install & Configure Memcached | |
apt-get install -y memcached | |
sed -i 's/-l 127.0.0.1/-l 0.0.0.0/' /etc/memcached.conf | |
service memcached restart | |
# Install & Configure Beanstalk | |
apt-get install -y --force-yes beanstalkd | |
sed -i "s/BEANSTALKD_LISTEN_ADDR.*/BEANSTALKD_LISTEN_ADDR=0.0.0.0/" /etc/default/beanstalkd | |
if grep START= /etc/default/beanstalkd; then | |
sed -i "s/#START=yes/START=yes/" /etc/default/beanstalkd | |
else | |
echo "START=yes" >> /etc/default/beanstalkd | |
fi | |
service beanstalkd start | |
sleep 5 | |
service beanstalkd restart | |
systemctl enable beanstalkd | |
# Configure Supervisor Autostart | |
systemctl enable supervisor.service | |
service supervisor start | |
# Configure Swap Disk | |
if [ -f /swapfile ]; then | |
echo "Swap exists." | |
else | |
fallocate -l 1G /swapfile | |
chmod 600 /swapfile | |
mkswap /swapfile | |
swapon /swapfile | |
echo "/swapfile none swap sw 0 0" >> /etc/fstab | |
echo "vm.swappiness=30" >> /etc/sysctl.conf | |
echo "vm.vfs_cache_pressure=50" >> /etc/sysctl.conf | |
fi | |
# Setup Unattended Security Upgrades | |
cat > /etc/apt/apt.conf.d/50unattended-upgrades << EOF | |
Unattended-Upgrade::Allowed-Origins { | |
"Ubuntu bionic-security"; | |
}; | |
Unattended-Upgrade::Package-Blacklist { | |
// | |
}; | |
EOF | |
cat > /etc/apt/apt.conf.d/10periodic << EOF | |
APT::Periodic::Update-Package-Lists "1"; | |
APT::Periodic::Download-Upgradeable-Packages "1"; | |
APT::Periodic::AutocleanInterval "7"; | |
APT::Periodic::Unattended-Upgrade "1"; | |
EOF | |
# Callback | |
curl --insecure --data "event_id=$event_id&server_id=$server_id&sudo_password=$my_root_pw&db_password=$my_db_pw&recipe_id=$recipe_id;" $callback_url |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment