Skip to content

Instantly share code, notes, and snippets.

@MostafaNorzade
Last active June 19, 2026 08:19
Show Gist options
  • Select an option

  • Save MostafaNorzade/0af3fdad035f027982e6c4aa9834a2fe to your computer and use it in GitHub Desktop.

Select an option

Save MostafaNorzade/0af3fdad035f027982e6c4aa9834a2fe to your computer and use it in GitHub Desktop.
#!/bin/bash
# setup-laravel-php84-ssh.sh
# Automated Laravel setup on Ubuntu with PHP 8.4 and SSH keys for GitLab (using non‑root deploy user)
set -e
if [[ $EUID -ne 0 ]]; then
echo "This script must be run with sudo!"
exit 1
fi
# ---------------------------------------------
# 1. System packages and PHP 8.4
# ---------------------------------------------
echo "➡️ Installing prerequisites and PHP 8.4..."
apt update && apt upgrade -y
apt install -y sudo curl gnupg lsb-release software-properties-common
add-apt-repository ppa:ondrej/php -y
apt update
apt install -y php8.4 php8.4-fpm php8.4-mbstring php8.4-xml php8.4-curl php8.4-zip \
php8.4-bcmath php8.4-mysql php8.4-cli php8.4-intl php8.4-gd \
unzip git composer redis-server mysql-server nginx acl supervisor
# ---------------------------------------------
# 2. Start and enable services
# ---------------------------------------------
for service in nginx php8.4-fpm redis-server mysql supervisor; do
systemctl enable --now $service
if ! systemctl is-active --quiet $service; then
echo "❌ Service $service is not active!"
exit 1
fi
done
# ---------------------------------------------
# 3. Database setup
# ---------------------------------------------
read -p "Enter Laravel database name: " DB_NAME
read -p "Enter database username: " DB_USER
read -s -p "Enter database password: " DB_PASS
echo
mysql -u root <<MYSQL_SCRIPT
CREATE DATABASE IF NOT EXISTS $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';
GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# ---------------------------------------------
# 4. Create deploy user
# ---------------------------------------------
if ! id -u deploy &>/dev/null; then
useradd -m -s /bin/bash deploy
echo "✅ User 'deploy' created."
else
if [ ! -d "/home/deploy" ]; then
mkdir -p /home/deploy
chown deploy:deploy /home/deploy
usermod -d /home/deploy deploy
echo "✅ Home directory for 'deploy' created."
fi
fi
mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chown -R deploy:deploy /home/deploy/.ssh
# ---------------------------------------------
# 5. SSH key for GitLab
# ---------------------------------------------
SSH_KEY="/home/deploy/.ssh/id_ed25519"
if [ ! -f "$SSH_KEY" ]; then
echo "➡️ Generating SSH key..."
sudo -u deploy ssh-keygen -t ed25519 -C "laravel-server-key" -f "$SSH_KEY" -N ""
echo "Add this public key to GitLab → Settings → SSH Keys:"
echo
cat "$SSH_KEY.pub"
echo
read -p "Press Enter after adding the key to GitLab..."
fi
# ---------------------------------------------
# 6. Git clone / pull
# ---------------------------------------------
PROJECT_DIR="/var/www/laminor"
read -p "Enter GitLab SSH URL: " GIT_SSH_URL
read -p "Enter branch name (default: master): " BRANCH_NAME
BRANCH_NAME=${BRANCH_NAME:-master}
if [ ! -d "$PROJECT_DIR" ]; then
sudo -u deploy git clone --branch "$BRANCH_NAME" --single-branch "$GIT_SSH_URL" "$PROJECT_DIR" \
|| { echo "❌ Git clone failed"; exit 1; }
else
cd "$PROJECT_DIR" || exit
sudo -u deploy git fetch origin "$BRANCH_NAME" || { echo "❌ git fetch failed"; exit 1; }
sudo -u deploy git checkout "$BRANCH_NAME" || { echo "❌ git checkout failed"; exit 1; }
sudo -u deploy git pull origin "$BRANCH_NAME" || { echo "❌ git pull failed"; exit 1; }
fi
cd "$PROJECT_DIR" || exit
# ---------------------------------------------
# 7. Composer install
# ---------------------------------------------
read -p "Enter username for nx.laminor.org: " COMPOSER_USERNAME
read -sp "Enter password/token for nx.laminor.org: " COMPOSER_PASSWORD
echo
sudo -u deploy composer config --global http-basic.nx.laminor.org "$COMPOSER_USERNAME" "$COMPOSER_PASSWORD"
sudo -u deploy composer install --no-dev --optimize-autoloader --no-interaction \
|| { echo "❌ Composer install failed"; exit 1; }
# ---------------------------------------------
# 8. .env configuration
# ---------------------------------------------
if [ -f ".env" ]; then
read -p ".env file exists, overwrite it? (y/N): " ENV_ANSWER
if [[ "$ENV_ANSWER" =~ ^[Yy]$ ]]; then
cp .env.example .env
else
echo "❌ Installation aborted."
exit 1
fi
else
cp .env.example .env
fi
sed -i "s/DB_DATABASE=.*/DB_DATABASE=$DB_NAME/" .env
sed -i "s/DB_USERNAME=.*/DB_USERNAME=$DB_USER/" .env
sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=$DB_PASS/" .env
sed -i "s/DB_HOST=.*/DB_HOST=127.0.0.1/" .env
sed -i "s/APP_KEY=.*/APP_KEY=/" .env
read -p "Enter APP_URL: " APP_URL
if [ -n "$APP_URL" ]; then
sed -i "s|APP_URL=.*|APP_URL=$APP_URL|" .env
fi
sed -i "s/REDIS_HOST=.*/REDIS_HOST=127.0.0.1/" .env
sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=null/" .env
sed -i "s/REDIS_PORT=.*/REDIS_PORT=6379/" .env
chown deploy:deploy "$PROJECT_DIR/.env"
# ---------------------------------------------
# 9. Disable IgnitionServiceProvider
# ---------------------------------------------
if grep -q "Spatie\\\\LaravelIgnition\\\\IgnitionServiceProvider" config/app.php; then
sed -i 's/\(.*Spatie\\LaravelIgnition\\IgnitionServiceProvider.*\)/\/\/ \1/' config/app.php
fi
# ---------------------------------------------
# 10. Laravel key and migrations
# ---------------------------------------------
sudo -u deploy php artisan key:generate || { echo "❌ key:generate failed"; exit 1; }
sudo -u deploy php artisan config:cache
sudo -u deploy php artisan route:cache
sudo -u deploy php artisan view:cache
sudo -u deploy php artisan migrate --seed || { echo "❌ migrate failed"; exit 1; }
# ---------------------------------------------
# 11. Permissions
# ---------------------------------------------
chown -R deploy:www-data "$PROJECT_DIR/storage" "$PROJECT_DIR/bootstrap/cache"
find "$PROJECT_DIR/storage" -type d -exec chmod 775 {} \;
find "$PROJECT_DIR/storage" -type f -exec chmod 664 {} \;
find "$PROJECT_DIR/bootstrap/cache" -type d -exec chmod 775 {} \;
find "$PROJECT_DIR/bootstrap/cache" -type f -exec chmod 664 {} \; 2>/dev/null || true
setfacl -R -m u:www-data:rwx "$PROJECT_DIR/storage/logs" 2>/dev/null || true
# ---------------------------------------------
# 12. Supervisor configuration
# ---------------------------------------------
echo "➡️ Configuring Supervisor..."
mkdir -p /var/log/supervisor/laravel
# Laravel Queue Worker
cat > /etc/supervisor/conf.d/laravel-worker.conf <<EOF
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php $PROJECT_DIR/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=deploy
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel/worker.log
stopwaitsecs=60
EOF
# Laravel Scheduler
cat > /etc/supervisor/conf.d/laravel-schedule.conf <<EOF
[program:laravel-schedule]
process_name=%(program_name)s
command=sh -c "php $PROJECT_DIR/artisan schedule:run --no-ansi && sleep 60"
autostart=true
autorestart=true
user=deploy
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel/schedule.log
EOF
# Reload supervisor
supervisorctl reread
supervisorctl update
supervisorctl start laravel-worker:*
supervisorctl start laravel-schedule
echo "✅ Supervisor configured:"
supervisorctl status
# ---------------------------------------------
# 13. Nginx configuration
# ---------------------------------------------
NGINX_AVAILABLE="/etc/nginx/sites-available/laminor.conf"
NGINX_ENABLED="/etc/nginx/sites-enabled/laminor.conf"
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
cat > "$NGINX_AVAILABLE" <<EOL
server {
listen 80;
server_name _;
root $PROJECT_DIR/public;
index index.php index.html;
location / {
try_files \$uri \$uri/ /index.php?\$query_string;
}
location ~ \.php\$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
fastcgi_read_timeout 300;
proxy_read_timeout 300;
}
EOL
rm -f "$NGINX_ENABLED"
ln -s "$NGINX_AVAILABLE" "$NGINX_ENABLED"
rm -f /etc/nginx/sites-enabled/default
nginx -t || { echo "❌ Nginx test failed"; exit 1; }
systemctl reload nginx
systemctl restart php8.4-fpm
echo ""
echo "✅ Laravel project is ready!"
echo "📌 Supervisor status:"
supervisorctl status
# ============================================================
# 🧰 دستورات کمکی – کامنت شده (برای استفاده بعدی)
# ============================================================
#
# --- انتقال فایل دیتابیس از لوکال به سرور ---
# scp core.sql ubuntu@IP_SERVER:~
#
# --- وارد کردن (import) فایل دیتابیس در دیتابیس موجود (تغییر با اسم واقعی) ---
# sudo mysql database_name < /home/ubuntu/core.sql
#
# --- یا با کاربر معمولی (اگر دسترسی SUPER نباشد) ---
# sed -i 's/\s*DEFINER=`[^`]*`@`[^`]*`//g' /home/ubuntu/core.sql
# mysql -u username -p database_name < /home/ubuntu/core.sql
#
# --- اگر خطای Table already exists رخ داد، می‌توانید جداول را قبل از import پاک کنید ---
# sudo -u deploy php artisan db:wipe
# سپس import را دوباره انجام دهید.
#
# --- تنظیمات دستی دیتابیس پس از import ---
# sudo mysql -u root -p
# CREATE DATABASE IF NOT EXISTS database_name;
# GRANT ALL PRIVILEGES ON database_name.* TO 'username'@'localhost';
# FLUSH PRIVILEGES;
#
# --- اصلاح مستقیم DB_HOST در فایل .env (در صورت بروز خطای اتصال به دیتابیس) ---
# sed -i "s/DB_HOST=.*/DB_HOST=127.0.0.1/" /var/www/laminor/.env
# sudo -u deploy php artisan config:clear
#
# --- بررسی وضعیت سرویس‌ها ---
# systemctl status nginx php8.4-fpm redis-server mysql
#
# --- مشاهده خطاهای Nginx ---
# tail -f /var/log/nginx/error.log
#
# --- مشاهده خطاهای PHP-FPM ---
# tail -f /var/log/php8.4-fpm.log
#
# --- خالی کردن کش لاراول ---
# cd /var/www/laminor
# sudo -u deploy php artisan config:clear
# sudo -u deploy php artisan cache:clear
# sudo -u deploy php artisan view:clear
# sudo systemctl restart php8.4-fpm
#
# ============================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment