Last active
January 15, 2025 08:19
-
-
Save AminulBD/9061f8a8c99e772249569aa1e6e5a362 to your computer and use it in GitHub Desktop.
Ubuntu Server LEMP Setup script
This file contains 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
#!/bin/bash | |
DEBIAN_FRONTEND="noninteractive" | |
# Default values for parameters | |
DEFAULT_PHP_VERSION=8.4 | |
DEFAULT_NODE_VERSION=22 | |
DEFAULT_DOMAIN="example.test" | |
DEFAULT_USER="example" | |
DEFAULT_MYSQL_PASSWORD=$(openssl rand -base64 10) | |
# Initialize parameters with default values | |
PHP_VERSION=$DEFAULT_PHP_VERSION | |
NODE_VERSION=$DEFAULT_NODE_VERSION | |
MYSQL_PASSWORD=$DEFAULT_MYSQL_PASSWORD | |
DOMAIN="" | |
CUSTOM_USER="" | |
# Function to display help text | |
show_help() { | |
echo "Usage: $0 [options]" | |
echo "" | |
echo "Options:" | |
echo " --domain= Specify the domain (default: $DEFAULT_DOMAIN)" | |
echo " --php= Specify the php version (default: $DEFAULT_PHP_VERSION)" | |
echo " --node= Specify the NodeJS version (default: $DEFAULT_NODE_VERSION)" | |
echo " --mysql-password= Specify the MySQL Password (default: Randomly generated)" | |
echo " --help Display this help and exit" | |
} | |
# Parse the command line arguments | |
for arg in "$@"; do | |
case $arg in | |
--php=*) | |
PHP_VERSION="${arg#*=}" | |
shift # Remove --php= from processing | |
;; | |
--node=*) | |
NODE_VERSION="${arg#*=}" | |
shift # Remove --node= from processing | |
;; | |
--mysql-password=*) | |
MYSQL_PASSWORD="${arg#*=}" | |
shift # Remove --mysql-password= from processing | |
;; | |
--domain=*) | |
DOMAIN="${arg#*=}" | |
shift # Remove --domain= from processing | |
;; | |
--user=*) | |
CUSTOM_USER="${arg#*=}" | |
shift # Remove --user= from processing | |
;; | |
--help) | |
show_help | |
exit 0 | |
;; | |
*) | |
echo "Unknown option: $arg" | |
show_help | |
exit 1 | |
;; | |
esac | |
done | |
if [ -z "$DOMAIN" ]; then | |
read -p "Enter domain [default: $DEFAULT_DOMAIN]: " DOMAIN | |
DOMAIN=${DOMAIN:-$DEFAULT_DOMAIN} | |
fi | |
if [ -z "$CUSTOM_USER" ]; then | |
read -p "Enter username [default: $DEFAULT_USER]: " CUSTOM_USER | |
CUSTOM_USER=${CUSTOM_USER:-$DEFAULT_USER} | |
fi | |
# configure mysql with random password | |
debconf-set-selections <<< "mysql-server mysql-server/root_password password $MYSQL_PASSWORD" | |
debconf-set-selections <<< "mysql-server mysql-server/root_password_again password $MYSQL_PASSWORD" | |
echo $MYSQL_PASSWORD > /root/.mysql_password | |
# install common packages | |
apt update -q && apt install -yq \ | |
apt-utils software-properties-common ca-certificates \ | |
cron gnupg git zip unzip curl wget \ | |
nginx certbot python3-certbot-nginx \ | |
redis-server supervisor mysql-server | |
# Add system user with custom home directory | |
useradd -m -d /srv/$CUSTOM_USER -s /bin/bash $CUSTOM_USER | |
mkdir -p /srv/$CUSTOM_USER/{apps,logs,ssl} | |
mkdir -p /srv/$CUSTOM_USER/apps/$DOMAIN/releases | |
mkdir -p /srv/$CUSTOM_USER/logs/{nginx,php,mysql,supervisor,ssl} | |
chown -R $CUSTOM_USER:$CUSTOM_USER /srv/$CUSTOM_USER | |
chmod 755 /srv/$CUSTOM_USER | |
# install php and extensions | |
add-apt-repository -y ppa:ondrej/php | |
apt install -yq \ | |
php$PHP_VERSION \ | |
php$PHP_VERSION-fpm \ | |
php$PHP_VERSION-mysql \ | |
php$PHP_VERSION-mbstring \ | |
php$PHP_VERSION-xml \ | |
php$PHP_VERSION-curl \ | |
php$PHP_VERSION-zip \ | |
php$PHP_VERSION-gd \ | |
php$PHP_VERSION-imagick \ | |
php$PHP_VERSION-bcmath \ | |
php$PHP_VERSION-redis \ | |
php$PHP_VERSION-intl \ | |
php$PHP_VERSION-soap \ | |
php$PHP_VERSION-sqlite3 \ | |
php$PHP_VERSION-mongodb | |
# create php-fpm pool | |
cat > /etc/php/$PHP_VERSION/fpm/pool.d/$CUSTOM_USER.conf <<EOF | |
[$CUSTOM_USER] | |
user = $CUSTOM_USER | |
group = $CUSTOM_USER | |
listen = /run/php/php$PHP_VERSION-fpm-$CUSTOM_USER.sock | |
listen.owner = www-data | |
listen.group = www-data | |
pm = dynamic | |
pm.max_children = 5 | |
pm.start_servers = 2 | |
pm.min_spare_servers = 1 | |
pm.max_spare_servers = 3 | |
access.log = /srv/$CUSTOM_USER/logs/php/$CUSTOM_USER.access.log | |
slowlog = /srv/$CUSTOM_USER/logs/php/$CUSTOM_USER.slow.log | |
php_admin_value[error_log] = /srv/$CUSTOM_USER/logs/php/$CUSTOM_USER.error.log | |
EOF | |
systemctl restart php$PHP_VERSION-fpm | |
# install composer | |
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer | |
# install wp-cli | |
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar | |
chmod +x wp-cli.phar && mv wp-cli.phar /usr/local/bin/wp | |
# install nodejs | |
sudo mkdir -p /etc/apt/keyrings | |
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg | |
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list | |
apt update -q && apt install -yq nodejs | |
# install yarn and pm2 | |
npm install -g yarn pm2 | |
# install laravel | |
cd /srv/$CUSTOM_USER/apps/$DOMAIN/releases | |
RELEASE_NAME=$(date +%s) | |
sudo -u $CUSTOM_USER composer create-project laravel/laravel $RELEASE_NAME | |
sudo -u $CUSTOM_USER ln -nfs /srv/$CUSTOM_USER/apps/$DOMAIN/releases/$RELEASE_NAME /srv/$CUSTOM_USER/apps/$DOMAIN/current | |
sudo -u $CUSTOM_USER mv /srv/$CUSTOM_USER/apps/$DOMAIN/releases/$RELEASE_NAME/storage /srv/$CUSTOM_USER/apps/$DOMAIN/storage | |
sudo -u $CUSTOM_USER mv /srv/$CUSTOM_USER/apps/$DOMAIN/releases/$RELEASE_NAME/.env /srv/$CUSTOM_USER/apps/$DOMAIN/.env | |
sudo -u $CUSTOM_USER ln -nfs /srv/$CUSTOM_USER/apps/$DOMAIN/.env /srv/$CUSTOM_USER/apps/$DOMAIN/releases/$RELEASE_NAME/.env | |
sudo -u $CUSTOM_USER ln -nfs /srv/$CUSTOM_USER/apps/$DOMAIN/storage /srv/$CUSTOM_USER/apps/$DOMAIN/releases/$RELEASE_NAME/storage | |
sudo -u $CUSTOM_USER ln -nfs /srv/$CUSTOM_USER/apps/$DOMAIN/storage/app/public /srv/$CUSTOM_USER/apps/$DOMAIN/releases/$RELEASE_NAME/public/storage | |
# create nginx config | |
cat > /etc/nginx/sites-available/$DOMAIN <<EOF | |
server { | |
listen 80; | |
listen [::]:80; | |
server_name $DOMAIN; | |
root /srv/$CUSTOM_USER/apps/$DOMAIN/current/public; | |
add_header X-Frame-Options "SAMEORIGIN"; | |
add_header X-Content-Type-Options "nosniff"; | |
index index.php; | |
charset utf-8; | |
location / { | |
try_files \$uri \$uri/ /index.php?\$query_string; | |
} | |
location = /favicon.ico { access_log off; log_not_found off; } | |
location = /robots.txt { access_log off; log_not_found off; } | |
error_log /srv/$CUSTOM_USER/logs/nginx/$DOMAIN.error.log; | |
access_log /srv/$CUSTOM_USER/logs/nginx/$DOMAIN.access.log; | |
error_page 404 /index.php; | |
location ~ \.php$ { | |
fastcgi_pass unix:/run/php/php$PHP_VERSION-fpm-$CUSTOM_USER.sock; | |
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; | |
include fastcgi_params; | |
} | |
location ~ /\.(?!well-known).* { | |
deny all; | |
} | |
} | |
EOF | |
# enable nginx config | |
ln -s /etc/nginx/sites-available/$DOMAIN /etc/nginx/sites-enabled/$DOMAIN | |
systemctl restart nginx | |
# install crontab | |
echo "* * * * * cd /srv/$CUSTOM_USER/apps/$DOMAIN/current && php artisan schedule:run >> /dev/null 2>&1" >> /var/spool/cron/crontabs/$CUSTOM_USER | |
chown $CUSTOM_USER:crontab /var/spool/cron/crontabs/$CUSTOM_USER | |
chmod 600 /var/spool/cron/crontabs/$CUSTOM_USER | |
systemctl restart cron | |
# install supervisor config | |
cat > /etc/supervisor/conf.d/$DOMAIN.conf <<EOF | |
[program:$DOMAIN] | |
process_name=%(program_name)s_%(process_num)02d | |
; if you are using horizon uncomment below line and comment queue worker line | |
;command=php /srv/$CUSTOM_USER/apps/$DOMAIN/current/artisan horizon | |
; if you are using queue worker uncomment below line and comment horizon line | |
command=php /srv/$CUSTOM_USER/apps/$DOMAIN/current/artisan queue:work --sleep=3 --tries=3 | |
autostart=true | |
autorestart=true | |
user=$CUSTOM_USER | |
numprocs=1 | |
redirect_stderr=true | |
stdout_logfile=/srv/$CUSTOM_USER/logs/supervisor/$DOMAIN.log | |
EOF | |
supervisorctl reread | |
supervisorctl update | |
echo "127.0.0.1 $DOMAIN" >> /etc/hosts |
How to use this:
- Download this script as root:
https://gist.githubusercontent.com/AminulBD/9061f8a8c99e772249569aa1e6e5a362/raw/bootstrap.sh
- Make it executable as:
chmod +x bootstrap.sh
- Run the script:
./bootstrap.sh
and follow the instructions.
Also, you can pass all info directly by: ./bootstrap.sh --domain=aminul.net --user=aminul
This script will generate mysql root password and will be stored in ~/.mysql_password
file.
# This script will install LEMP (w/ composer & wp cli) and Node (w/ PM2 & Yarn).
# This setup is similar as laravel forge
# It will also setup fresh laravel application, cron and supervisor.
# FYI: This setup is my personalized setup with custom workflows.
# All files are located in: /srv/$USER/apps/$DOMAIN/
# Install LEMP and Node on your server just one click.
curl -fsSL amn.pw/lemp | bash -s -- --domain=example.com --user=example
# Or you can download and run
wget amn.pw/lemp
chmod +x lemp
./lemp --domain=example.com --user=example
# to see help
./lemp --help
Import SSH public keys from GitHub:
# you can replace your username. I've used my one.
curl -fsSL https://github.com/aminulbd.keys >> ~/.ssh/authorized_keys
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
curl -fsSL https://gist.githubusercontent.com/AminulBD/9061f8a8c99e772249569aa1e6e5a362/raw/bootstrap.sh | bash -s