📥 Git-based Deploy
# SSH key for GitHub
ssh-keygen -t ed25519 -f ~/.ssh/github_deploy
# Add ~/.ssh/github_deploy.pub to GitHub Deploy Keys
# Clone
cd /var/www
sudo git clone git@github.com:user/myapp.git
sudo chown -R deploy:www-data myapp
🐘 Laravel Deploy
cd /var/www/myapp
# Composer install
composer install --no-dev --optimize-autoloader
# .env setup
cp .env.example .env
nano .env
# DB_CONNECTION=mysql
# DB_DATABASE=myapp
# APP_ENV=production
# APP_DEBUG=false
# APP_URL=https://example.com
php artisan key:generate
php artisan migrate --force
php artisan storage:link
# Cache for production
php artisan config:cache
php artisan route:cache
php artisan view:cache
# Queue worker (systemd service)
# /etc/systemd/system/laravel-queue.service
[Service]
User=deploy
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/php artisan queue:work --sleep=3 --tries=3
Restart=always
# Schedule
* * * * * cd /var/www/myapp && php artisan schedule:run >/dev/null 2>&1
# Permission fix
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache
🟢 Node.js Deploy with PM2
cd /var/www/api
npm ci --production
# OR pnpm install --prod
# .env file
nano .env
# PM2 ecosystem
nano ecosystem.config.js
module.exports = {
apps: [{
name: "api",
script: "./server.js",
instances: "max", // = CPU count
exec_mode: "cluster",
env: {
NODE_ENV: "production",
PORT: 3000
},
error_file: "./logs/err.log",
out_file: "./logs/out.log",
max_memory_restart: "500M"
}]
};
pm2 start ecosystem.config.js
pm2 save
pm2 startup # follow output, paste cmd
pm2 list
pm2 logs api
pm2 monit
pm2 reload api # zero-downtime restart
🐍 Django Deploy
cd /var/www/django-app
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt gunicorn
# .env
DJANGO_SECRET_KEY=...
DEBUG=False
ALLOWED_HOSTS=example.com
python manage.py migrate
python manage.py collectstatic --noinput
# Gunicorn systemd service
[Service]
User=deploy
WorkingDirectory=/var/www/django-app
ExecStart=/var/www/django-app/venv/bin/gunicorn \
--workers 4 --bind unix:/run/gunicorn.sock \
myapp.wsgi:application
Restart=always
# Nginx proxy
location / {
proxy_pass http://unix:/run/gunicorn.sock;
include proxy_params;
}
🔐 .env Security
chmod 600 .env
chown deploy:deploy .env
# .gitignore
.env
.env.production
# Nginx block access
location ~ /\.env {
deny all;
}
🚀 Zero-Downtime Deploy Script
#!/bin/bash
set -euo pipefail
cd /var/www/myapp
php artisan down # maintenance mode
git fetch origin main
git reset --hard origin/main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
sudo systemctl reload php8.2-fpm
php artisan up # back online
# Restart queue workers
sudo systemctl restart laravel-queue
🎯 Atomic Symlink Deploy (advanced)
# Releases pattern (Capistrano-style)
/var/www/myapp/
├── releases/
│ ├── 20240101-120000/ # old
│ └── 20240102-100000/ # new
├── shared/
│ ├── .env
│ └── storage/
└── current → releases/20240102-100000 # symlink
# Deploy = create new release dir, swap symlink atomically
ln -sfn /var/www/myapp/releases/$NEW_VERSION /var/www/myapp/current
🎓 মূল কথা ⭐
Git pull + cache + reload = 5 min deploy। PM2 cluster = zero-downtime। .env permission 600 mandatory। Symlink-based deploy = rollback 1 sec।