๐ Tutorial Lengkap: Setup CI/CD Laravel dengan GitHub Actions - Zero Downtime Deployment
Halo developer! ๐
Pernah nggak sih kalian deploy aplikasi Laravel, terus website mati beberapa detik? User complain, boss marah-marah, dan kalian cuma bisa bilang "Bentar pak, lagi deploy". Nah, artikel ini bakal ngajarin kalian cara deploy Laravel dengan ZERO DOWNTIME menggunakan GitHub Actions. Jadi website tetep hidup meskipun lagi deploy. Keren kan?
๐ Table of Contents
- Kenapa Harus CI/CD?
- What is Zero Downtime Deployment?
- Requirements
- Arsitektur Deployment
- Step 1: Server Preparation
- Step 2: GitHub Actions Setup
- Step 3: aaPanel Configuration (Bonus)
- Step 4: Testing Deployment
- Step 5: Rollback Strategy
- Troubleshooting
- Pro Tips & Best Practices
๐ค Kenapa Harus CI/CD?
Bayangin kalian deploy manual kayak gini:
Pull latest code
git pull origin master
Install dependencies
composer install npm run build
Run migrations
php artisan migrate
Clear cache
php artisan cache:clear
Website mati 30 detik - 2 menit ๐
</CodeBlock>
<Alert variant="destructive">
<AlertTitle>Masalahnya:</AlertTitle>
<AlertDescription>
- โ Website mati sementara (downtime)<br/>
- โ Kalau gagal, user lihat error page<br/>
- โ Manual = cape = prone to human error<br/>
- โ Rollback susah kalau ada masalah
</AlertDescription>
</Alert>
<Alert variant="success">
<AlertTitle>Dengan CI/CD + Zero Downtime:</AlertTitle>
<AlertDescription>
- โ
Website tetap online 24/7<br/>
- โ
Otomatis deploy setiap push ke GitHub<br/>
- โ
Rollback cepat kalau ada masalah<br/>
- โ
Tidur nyenyak karena deploy aman
</AlertDescription>
</Alert>
---
## ๐ฏ What is Zero Downtime Deployment?
Konsepnya simpel tapi genius. Instead of updating files directly, kita pakai **symlink strategy**:
<FileTree>
Production Server: โโโ current -> releases/release_20250116_120000 โ User akses ini โโโ releases/ โ โโโ release_20250116_120000/ โ NEW (sedang prepare) โ โโโ release_20250116_110000/ โ OLD (sedang jalan) โโโ shared/ โโโ .env โโโ storage/
</FileTree>
<Callout type="success" title="Deployment Process">
1. Upload release baru ke folder `releases/release_TIMESTAMP`
2. Setup symlink, install dependencies, migrate database
3. **SWITCH** symlink `current` ke release baru (atomic, 0.001 detik!)
4. Website langsung pakai release baru
5. Cleanup old releases (keep 5 releases terakhir)
</Callout>
Website nggak pernah mati karena perpindahannya **instant**! ๐
---
## ๐ Requirements
Sebelum mulai, pastikan kalian punya:
<Tabs defaultValue="local">
<TabsList>
<TabsTrigger value="local">Local Development</TabsTrigger>
<TabsTrigger value="server">Production Server</TabsTrigger>
</TabsList>
<TabsContent value="local">
- โ
Laravel project (Laravel 10/11)
- โ
Git repository di GitHub
- โ
Terminal / Command Prompt
- โ
Kopi/teh โ (biar semangat)
</TabsContent>
<TabsContent value="server">
- โ
VPS (Ubuntu 20.04 / 22.04 / 24.04)
- โ
PHP 8.2+
- โ
MySQL / MariaDB
- โ
Nginx / Apache
- โ
Composer
- โ
Node.js & NPM
- โ
SSH access
</TabsContent>
</Tabs>
<Callout type="info">
Tutorial ini support aaPanel, tapi bisa juga untuk VPS biasa atau hosting control panel lainnya.
</Callout>
---
## ๐๏ธ Arsitektur Deployment
Mari kita lihat **big picture**-nya dulu:
```mermaid
graph TB
A[Developer Push Code] -->|git push| B[GitHub Repository]
B -->|Trigger| C[GitHub Actions]
C -->|1. Build Assets| D[npm run build]
C -->|2. Install Dependencies| E[composer install]
C -->|3. Create Bundle| F[Release Package]
F -->|SSH Upload| G[Production Server]
G -->|4. Extract| H[/releases/new]
H -->|5. Symlink| I[/current]
I -->|6. Serve| J[Nginx/Apache]
J --> K[Users ๐]
style C fill:#2ea44f
style G fill:#0969da
style K fill:#ffd700
๐ ๏ธ Step 1: Server Preparation
1.1 - Create Directory Structure
SSH ke server kalian, terus jalankan:
Buat struktur folder
sudo mkdir -p $DEPLOY_PATH/releases sudo mkdir -p $DEPLOY_PATH/shared/storage/app/public sudo mkdir -p $DEPLOY_PATH/shared/storage/logs sudo mkdir -p $DEPLOY_PATH/shared/storage/framework/cache sudo mkdir -p $DEPLOY_PATH/shared/storage/framework/sessions sudo mkdir -p $DEPLOY_PATH/shared/storage/framework/views
Set ownership (ganti 'www' dengan user web server kalian)
sudo chown -R www:www $DEPLOY_PATH sudo chmod -R 755 $DEPLOY_PATH sudo chmod -R 775 $DEPLOY_PATH/shared/storage
</CodeBlock>
<Callout type="warning">
**Pro tip:** Kalau pakai aaPanel, user-nya biasanya `www`. Kalau VPS biasa bisa `www-data` atau `ubuntu`.
</Callout>
### 1.2 - Setup .env File
<CodeBlock language="bash" title="Create Environment File">
```bash
# Buat file .env di shared directory
sudo nano /var/www/production/shared/.env
Paste konfigurasi ini (sesuaikan dengan database kalian):
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=your_database DB_USERNAME=your_username DB_PASSWORD=super_secret_password
CACHE_DRIVER=file QUEUE_CONNECTION=database SESSION_DRIVER=file
... config lainnya
</CodeBlock>
<Alert variant="warning">
<AlertTitle>Security First!</AlertTitle>
<AlertDescription>
Jangan lupa set proper permissions:
```bash
sudo chmod 600 /var/www/production/shared/.env
sudo chown www:www /var/www/production/shared/.env
```
</AlertDescription>
</Alert>
### 1.3 - Database Setup
Kalau belum punya database, buat dulu:
<CodeBlock language="sql" title="MySQL Database Setup">
```bash
# Login ke MySQL
mysql -u root -p
# Buat database dan user
CREATE DATABASE laravel_production
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE USER 'laravel_user'@'localhost'
IDENTIFIED BY 'your_secure_password';
GRANT ALL PRIVILEGES ON laravel_production.*
TO 'laravel_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Test koneksi:
1.4 - SSH Key Configuration
Di komputer lokal kalian:
Copy ke server
ssh-copy-id www@your-server-ip
Test SSH
ssh www@your-server-ip "echo 'Connected!'"
</CodeBlock>
<Callout type="info">
**Important:** Nanti private key-nya akan kita masukkan ke GitHub Secrets.
</Callout>
### 1.5 - Supervisor Setup (Queue Workers)
<CodeBlock language="bash" title="Install Supervisor">
```bash
# Install supervisor
sudo apt install supervisor -y
# Buat config file
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
Paste configuration ini:
Start supervisor:
โ๏ธ Step 2: GitHub Actions Setup
Sekarang bagian yang seru! Kita bikin automated deployment workflow di GitHub.
2.1 - Configure GitHub Secrets
Buka repository GitHub kalian, terus:
- Klik Settings
- Klik Secrets and variables โ Actions
- Klik New repository secret
Tambahkan secrets berikut:
| Secret Name | Value | Example |
|---|---|---|
SSH_PRIVATE_KEY |
Private SSH key kalian | -----BEGIN RSA PRIVATE KEY-----... |
SSH_USERNAME |
Username SSH | www atau ubuntu |
SSH_HOST |
IP/hostname server | 192.168.1.100 |
SSH_PORT |
SSH port | 22 |
PRODUCTION_PATH |
Path deployment production | /var/www/production |
DEVELOPMENT_PATH |
Path deployment development | /var/www/development |
2.2 - Create Workflow File
Di repository Laravel kalian, buat file structure ini:
Paste workflow configuration ini:
on: push: branches: - master # Deploy ke production - development # Deploy ke development
env: KEEP_RELEASES: 5 PHP_VERSION: '8.2' NODE_VERSION: '20'
jobs: build_and_deploy: name: Build & Zero-downtime Deploy runs-on: ubuntu-latest
steps:
- name: ๐ฅ Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: ๐ Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: mbstring, xml, ctype, curl, bcmath, pdo_mysql, gd, zip
coverage: none
- name: ๐ฆ Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: ๐ต Get Composer Cache Directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: ๐พ Cache Composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: ๐ผ Install Composer dependencies
run: composer install --no-interaction --prefer-dist --no-dev --optimize-autoloader
- name: ๐ฆ Install NPM dependencies
run: npm ci
- name: ๐๏ธ Build assets with Vite
run: npm run build
- name: ๐ฆ Prepare release bundle
id: prepare_release
run: |
TIMESTAMP=$(date +%Y%m%d%H%M%S)
RELEASE_DIR="release_${TIMESTAMP}"
mkdir -p "$RELEASE_DIR"
rsync -a \
--exclude='.git' \
--exclude='node_modules' \
--exclude='tests' \
--exclude='.github' \
./ "$RELEASE_DIR/"
mkdir -p "$RELEASE_DIR/storage/logs"
mkdir -p "$RELEASE_DIR/bootstrap/cache"
echo "RELEASE_DIR=$RELEASE_DIR" >> $GITHUB_OUTPUT
- name: ๐ Add SSH key
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: ๐ Deploy to server
env:
SSH_USER: ${{ secrets.SSH_USERNAME }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PORT: ${{ secrets.SSH_PORT }}
PRODUCTION_PATH: ${{ secrets.PRODUCTION_PATH }}
DEVELOPMENT_PATH: ${{ secrets.DEVELOPMENT_PATH }}
run: |
# ... deployment script (check full version di repo)
echo "๐ Deployment successful!"
- name: โ
Verify deployment
run: echo "Website is running!"
</CodeBlock>
<Callout type="success">
Check full workflow code di repository atau download dari link di akhir artikel!
</Callout>
### 2.3 - Commit & Push
<CodeBlock language="bash" title="Deploy Your Workflow">
```bash
git add .github/workflows/deploy.yml
git commit -m "feat: add CI/CD deployment workflow ๐"
git push origin master
Sekarang cek tab Actions di GitHub repository kalian. Kalau semua benar, akan ada workflow yang sedang running! ๐
๐จ Step 3: aaPanel Configuration (Bonus)
Kalau kalian pakai aaPanel, ada konfigurasi khusus yang perlu dilakukan.
3.1 - Update Nginx Configuration
# PENTING: Point ke current/public (symlink)
root /www/wwwroot/your-domain.com/current/public;
index index.php index.html;
charset utf-8;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Laravel routing
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/tmp/php-cgi-82.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
</CodeBlock>
</TabsContent>
<TabsContent value="apache">
<CodeBlock language="apache" title="Apache Configuration">
```apache
<VirtualHost *:80>
ServerName your-domain.com
DocumentRoot /www/wwwroot/your-domain.com/current/public
<Directory /www/wwwroot/your-domain.com/current/public>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
</CodeBlock>
3.2 - Setup Sudo Permissions
Tambahkan baris ini di paling bawah:
3.3 - SSL Certificate
Boom! Website kalian sekarang HTTPS! ๐
๐งช Step 4: Testing Deployment
Saatnya test deployment system kita!
git add . git commit -m "test: CI/CD deployment ๐" git push origin master
</CodeBlock>
<Callout type="success">
Buka GitHub โ tab **Actions**, watch the magic happen! ๐ฌ
</Callout>
</TabsContent>
<TabsContent value="manual">
<CodeBlock language="bash" title="Manual Deployment Test">
```bash
export SSH_USER="www"
export SSH_HOST="192.168.1.100"
export DEPLOY_PATH="/var/www/production"
chmod +x deploy-test.sh
./deploy-test.sh
</CodeBlock>
Verify Deployment
Check current release
ls -la /var/www/production/current
Check Laravel version
cd /var/www/production/current php artisan --version
View logs
tail -f storage/logs/laravel.log
</CodeBlock>
Buka website kalian di browser. Kalau muncul, berarti **SUKSES!** ๐๐
---
## ๐ Step 5: Rollback Strategy
Waduh, ada bug di production? **Don't panic!** Kita bisa rollback dengan cepat.
### Quick Rollback (Manual)
<CodeBlock language="bash" title="Emergency Rollback">
```bash
# SSH ke server
ssh www@your-server-ip
# List available releases
ls -lt /var/www/production/releases
# Switch ke release sebelumnya
cd /var/www/production
ln -sfn releases/release_20250116110000 current
# Clear cache
cd current
php artisan config:clear
php artisan cache:clear
php artisan queue:restart
Website langsung balik ke versi sebelumnya dalam hitungan detik!
Automated Rollback Script
chmod +x rollback.sh ./rollback.sh
</CodeBlock>
<Callout type="success" title="Auto Rollback Features">
Script ini akan otomatis:
1. โ
Tampilkan list releases
2. โ
Switch ke release sebelumnya
3. โ
Clear cache
4. โ
Restart workers
</Callout>
---
## ๐ Troubleshooting
### Problem 1: "Permission Denied"
<Alert variant="destructive">
<AlertTitle>Error:</AlertTitle>
<AlertDescription>
```
Permission denied (publickey)
```
</AlertDescription>
</Alert>
<Callout type="info" title="Solution">
```bash
# Pastikan SSH key sudah di-add ke server
ssh-copy-id www@your-server-ip
# Test koneksi
ssh www@your-server-ip "echo 'Works!'"
Problem 2: "Database Connection Failed"
Recreate user
DROP USER IF EXISTS 'laravel_user'@'localhost'; CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'new_password'; GRANT ALL ON laravel_production.* TO 'laravel_user'@'localhost'; FLUSH PRIVILEGES;
</CodeBlock>
</TabsContent>
</Tabs>
### Problem 3: "Assets Not Loading (404)"
<Callout type="warning" title="Fix Storage Symlink">
```bash
cd /var/www/production/current
rm -f public/storage
ln -sfn ../storage/app/public public/storage
chmod -R 775 storage
Common Issues Reference
| Issue | Quick Fix | Script |
|---|---|---|
| Database error | ./fix-database.sh |
Auto-detect & fix |
| Permission denied | chown -R www:www /path |
Fix ownership |
| Assets 404 | Recreate storage symlink | ln -sfn |
| Queue stuck | supervisorctl restart all |
Restart workers |
๐ก Pro Tips & Best Practices
1. Multiple Environments
Setup berbagai environment untuk workflow yang lebih baik:
2. Slack/Discord Notification
Get notified setiap deployment:
3. Run Tests Before Deploy
Safety first! Test dulu sebelum deploy:
Kalau test gagal, deployment otomatis dibatalkan.
4. Database Backup Strategy
Keep 30 days backup
find /backup -name "*.sql" -mtime +30 -delete
</CodeBlock>
### 5. Health Check After Deploy
Pastikan website beneran jalan:
<CodeBlock language="yaml" title="Health Check Step">
```yaml
- name: ๐ฅ Health Check
run: |
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" https://your-domain.com)
if [ $RESPONSE -ne 200 ]; then
echo "Health check failed! Rolling back..."
exit 1
fi
๐ Conclusion
Selamat! ๐ Kalian sekarang punya deployment system yang:
What's Next?
- Setup monitoring (Sentry, New Relic)
- Configure CDN (Cloudflare)
- Implement caching (Redis, Memcached)
- Add automated testing
- Setup staging environment
๐ฅ Download Resources
๐ FAQ
๐ External Resources
Tags: #Laravel #CI/CD #GitHub #Actions #Deployment #ZeroDowntime #DevOps #PHP #Tutorial #Indonesian
Artikel ini ditulis dengan โค๏ธ dan banyak โ by developer yang udah capek deploy manual.
Last updated: 16 Januari 2025