# Deployment Guide Panduan instalasi aplikasi di server produksi **Ubuntu 22.04+**. --- ## Spesifikasi Server | Komponen | Minimum | Rekomendasi | |----------|---------|-------------| | CPU | 2 core | 4 core | | RAM | 4 GB | 8 GB | | Storage | 40 GB SSD | 100 GB SSD | | OS | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS | --- ## Langkah 1 — Install Dependensi ```bash sudo apt update && sudo apt upgrade -y # PHP 8.3 + ekstensi sudo add-apt-repository ppa:ondrej/php -y sudo apt install -y php8.3 php8.3-fpm php8.3-pgsql php8.3-redis \ php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip php8.3-gd \ php8.3-bcmath php8.3-intl php8.3-readline # PostgreSQL, Redis, Nginx, Supervisor sudo apt install -y postgresql postgresql-contrib redis-server nginx supervisor sudo systemctl enable redis-server # Node.js 20 curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt install -y nodejs # Composer curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer ``` --- ## Langkah 2 — Setup Database ```bash sudo -u postgres psql ``` ```sql CREATE USER biiproject WITH PASSWORD 'password_kuat_anda'; CREATE DATABASE biiproject_db OWNER biiproject; GRANT ALL PRIVILEGES ON DATABASE biiproject_db TO biiproject; \q ``` --- ## Langkah 3 — Deploy Aplikasi ```bash cd /var/www sudo git clone html sudo chown -R $USER:www-data html cd html composer install --no-dev --optimize-autoloader npm install && npm run build cp .env.example .env php artisan key:generate ``` ### Edit `.env` ```env APP_NAME="biiproject" APP_ENV=production APP_DEBUG=false APP_URL=https://domain.com DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 DB_DATABASE=biiproject_db DB_USERNAME=biiproject DB_PASSWORD=password_kuat_anda CACHE_STORE=redis SESSION_DRIVER=redis QUEUE_CONNECTION=redis BROADCAST_CONNECTION=reverb REDIS_HOST=127.0.0.1 REDIS_PORT=6379 REVERB_APP_ID=your-app-id REVERB_APP_KEY=your-app-key REVERB_APP_SECRET=your-app-secret REVERB_HOST=domain.com REVERB_PORT=8080 REVERB_SCHEME=https BCRYPT_ROUNDS=12 # Opsional — Error monitoring SENTRY_LARAVEL_DSN=https://xxxx@sentry.io/xxxx SENTRY_TRACES_SAMPLE_RATE=0.1 SENTRY_PROFILES_SAMPLE_RATE=0.1 ``` ### Migrasi, Seed & Optimisasi ```bash php artisan migrate --force php artisan db:seed --force php artisan cache:clear # wajib setelah seeder php artisan storage:link php artisan optimize php artisan l5-swagger:generate # regen API docs ``` ### Post-Deploy Verification ```bash # Quality gate — pastikan tidak ada regresi ./vendor/bin/phpstan analyse --memory-limit=1G ./vendor/bin/pint --test composer audit --no-dev # Health check curl -sf https://domain.com/api/health | jq . # expect: {"status":"healthy","timestamp":"...","checks":{...}} ``` --- ## Langkah 4 — Konfigurasi Nginx `/etc/nginx/sites-available/biiproject`: ```nginx server { listen 80; server_name domain.com; root /var/www/html/public; index index.php; client_max_body_size 50M; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } # WebSocket Reverb location /app { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 60s; } location ~ /\.(?!well-known) { deny all; } } ``` > **Catatan keamanan:** Laravel mengirim header `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, dan `Permissions-Policy` dari middleware `SecurityHeaders`. Tidak perlu duplikasi di Nginx — biarkan aplikasi yang kontrol (lebih mudah di-update via setting). ```bash sudo ln -s /etc/nginx/sites-available/biiproject /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx ``` --- ## Langkah 5 — SSL (Let's Encrypt) ```bash sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d domain.com ``` Setelah HTTPS aktif, **aktifkan HSTS** dari admin panel: Global Settings → Login Security → `HSTS Enabled`. Aplikasi akan mengirim header `Strict-Transport-Security: max-age=31536000; includeSubDomains; preload`. --- ## Langkah 6 — Background Workers (Supervisor) ### Queue Worker `/etc/supervisor/conf.d/biiproject-worker.conf`: ```ini [program:biiproject-worker] process_name=%(program_name)s_%(process_num)02d command=php /var/www/html/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600 autostart=true autorestart=true user=www-data numprocs=2 redirect_stderr=true stdout_logfile=/var/www/html/storage/logs/worker.log ``` ### Reverb (WebSocket) `/etc/supervisor/conf.d/biiproject-reverb.conf`: ```ini [program:biiproject-reverb] command=php /var/www/html/artisan reverb:start --host=0.0.0.0 --port=8080 autostart=true autorestart=true user=www-data redirect_stderr=true stdout_logfile=/var/www/html/storage/logs/reverb.log ``` ### Scheduler (Cron) ```bash echo "* * * * * cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1" \ | sudo crontab -u www-data - ``` > Scheduler menjalankan `dashboard:broadcast-stats` setiap menit — membutuhkan queue worker dan Reverb **sudah berjalan** agar broadcast terkirim ke browser. Pastikan keduanya distart via Supervisor sebelum mengaktifkan cron. ### Aktifkan ```bash sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start all ``` --- ## Langkah 7 — Permission File ```bash sudo chown -R www-data:www-data /var/www/html sudo chmod -R 755 /var/www/html/storage /var/www/html/bootstrap/cache ``` --- ## Langkah 8 — Konfigurasi Pasca-Deploy ### A. Global Settings Login sebagai Super Admin → System Settings → Global Settings. Pastikan: - [ ] `Password Policy`: min 12, charset mixed-case + digit + symbol, expiry & history sesuai kebijakan - [ ] `Login Security`: max attempts 5, lockout duration, 2FA toggle, captcha jika perlu - [ ] `IP & Access`: whitelist admin (IP kantor/VPN), auto-block on burst - [ ] `HSTS Enabled`: ON (setelah SSL aktif) - [ ] `Single Session`: ON jika kebijakan satu-device-per-akun ### B. Verifikasi Quality Gate ```bash # Pastikan dependencies aman composer audit --no-dev --abandoned=ignore # Pastikan tidak ada regresi php artisan test --parallel ``` --- ## Layanan Eksternal (Opsional) Sebagian besar bisa diatur via **Global Settings** di admin panel — tidak perlu edit `.env`. ### Email SMTP ```env MAIL_MAILER=smtp MAIL_HOST=smtp.mailgun.org MAIL_PORT=587 MAIL_USERNAME=postmaster@domain.com MAIL_PASSWORD=xxxxxxxx MAIL_ENCRYPTION=tls MAIL_FROM_ADDRESS=noreply@domain.com MAIL_FROM_NAME="biiproject" ``` ### Sentry (Error Monitoring) ```env SENTRY_LARAVEL_DSN=https://xxxx@o0.ingest.sentry.io/xxxx SENTRY_TRACES_SAMPLE_RATE=0.1 ``` Buat project di [sentry.io](https://sentry.io), pilih platform **Laravel**, dan salin DSN-nya. ### Telegram Bot 1. Chat `@BotFather` → `/newbot` → simpan **Token** 2. Kirim pesan ke bot, buka `https://api.telegram.org/bot/getUpdates` → simpan **Chat ID** 3. Masukkan via Global Settings → Notifications > **Catatan:** `IpAccessControl` middleware mengirim alert otomatis ke Telegram saat sebuah IP di-auto-block karena burst — pastikan token + chat id valid. ### Google Drive Backup 1. Google Cloud Console → buat project → enable **Google Drive API** 2. Buat **OAuth 2.0 Client ID** (Desktop app) 3. Dapatkan **Refresh Token** via OAuth Playground 4. Masukkan Client ID, Client Secret, Refresh Token, dan nama folder via Global Settings → Backup ### Amazon S3 1. Buat IAM user dengan policy `AmazonS3FullAccess` 2. Buat S3 bucket 3. Masukkan Access Key, Secret, Bucket, Region (dan endpoint opsional) via Global Settings → Backup ### Firebase Cloud Messaging (Push Notification Mobile) 1. Buat project di Firebase Console → Project Settings → Cloud Messaging 2. Unduh `google-services.json` (Android) atau `GoogleService-Info.plist` (iOS) 3. Letakkan di direktori mobile app sesuai panduan Expo 4. Device token diregistrasikan otomatis via API `/api/v1/devices/register` --- ## CI/CD Workflow di `.github/workflows/ci.yml`. Setiap push ke `main`/`develop`/`config`/`advanced` dan setiap PR ke `main`/`develop` menjalankan: 1. **Test** — `php artisan test --parallel` (Postgres 15 + Redis 7 service containers) 2. **Lint** — `pint --test` + `composer audit --abandoned=ignore` + `permissions:audit` 3. **Static Analysis** — `phpstan analyse --memory-limit=1G` Branch protection: PR tidak bisa di-merge kalau salah satu job gagal. --- ## Update Aplikasi Cara tercepat dan teraman untuk memperbarui aplikasi adalah menggunakan skrip deployment otomatis yang sudah disediakan: ```bash cd /var/www/html sudo chmod +x deploy.sh ./deploy.sh ``` Skrip ini akan melakukan: 1. Masuk ke Maintenance Mode. 2. Update dependencies (Composer & NPM). 3. Build assets (Vite). 4. Run migrations. 5. Optimasi cache & pembersihan log. 6. Restart background workers (Queue & Reverb). 7. Verifikasi kesehatan sistem. 8. Keluar dari Maintenance Mode. ### Update Manual ```bash cd /var/www/html git pull origin main php artisan down --secret="your-bypass-key" composer install --no-dev --optimize-autoloader npm ci && npm run build php artisan migrate --force php artisan cache:clear php artisan config:cache php artisan route:cache php artisan view:cache php artisan event:cache php artisan l5-swagger:generate sudo supervisorctl restart biiproject-worker:* sudo supervisorctl restart biiproject-reverb # Verifikasi curl -sf https://domain.com/api/health | jq -e '.checks.database.status == "ok"' php artisan up ``` --- ## Deployment via Docker (alternatif) ```bash git clone Project cd Project cp .env.example .env # Edit .env: # DB_HOST=pgsql (nama service Docker, bukan 127.0.0.1) # REDIS_HOST=redis ./vendor/bin/sail up -d ./vendor/bin/sail artisan migrate --seed --force ./vendor/bin/sail artisan cache:clear ./vendor/bin/sail artisan optimize ``` > **Penting:** Semua perintah `php artisan` di lingkungan Docker **harus** dijalankan via `./vendor/bin/sail artisan`, bukan langsung. Host `pgsql` hanya bisa di-resolve dari dalam Docker network. --- ## Troubleshooting | Masalah | Solusi | |---------|--------| | 502 Bad Gateway | `sudo systemctl status php8.3-fpm` | | Queue tidak jalan | `sudo supervisorctl status` | | Permission denied | `sudo chown -R www-data:www-data storage bootstrap/cache` | | Cache error setelah update | `php artisan optimize:clear && php artisan optimize` | | Settings tidak muncul setelah seeder | `php artisan cache:clear` (settings di-cache 60 menit) | | WebSocket gagal connect | `sudo supervisorctl status biiproject-reverb` + cek port 8080 di firewall | | `pgsql` host tidak resolve | Pastikan `DB_HOST=127.0.0.1` (bukan `pgsql`) di server non-Docker | | Telescope/log tidak ditemukan | Cek `storage/logs/laravel.log` & menu System Monitoring → Logs | | Error monitoring tidak aktif | Pastikan `SENTRY_LARAVEL_DSN` sudah diset di `.env` | | `/api/health` return 503 | Cek setiap field `checks.*.status` — yang `fail` mengindikasikan masalah. `warn` (storage >90%) tetap 200 by design. | | OAuth callback 404 | Pastikan route order: `/auth/callback` HARUS sebelum `/auth/{provider}` di `routes/web.php`. | | `column "google_id" does not exist` | Pastikan migrasi `2026_05_12_120000_add_social_columns_to_users_table` sudah jalan. | | Dashboard stats tidak update real-time | Cek Reverb berjalan (`supervisorctl status biiproject-reverb`), queue worker aktif, dan `BROADCAST_CONNECTION=reverb` di `.env`. | | Dashboard widget tidak tersimpan | Pastikan migrasi `2026_05_16_220000_create_dashboard_widget_preferences_table` sudah dijalankan. | --- ## Performance Tuning ### PHP-FPM `/etc/php/8.3/fpm/pool.d/www.conf`: ```ini pm = dynamic pm.max_children = 50 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 ``` ### OPcache `/etc/php/8.3/fpm/php.ini`: ```ini opcache.enable=1 opcache.memory_consumption=256 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=20000 opcache.validate_timestamps=0 ; production only opcache.preload=/var/www/html/bootstrap/cache/preload.php opcache.preload_user=www-data ``` ### Redis Tuning `/etc/redis/redis.conf`: ``` maxmemory 1gb maxmemory-policy allkeys-lru ``` ### PostgreSQL Tambah index recommendation: sudah ada (lihat migrasi `2026_05_14_100000_add_performance_indexes`). Untuk dataset besar (>10M rows pada `activity_log`), pertimbangkan `pg_partman` untuk partisi bulanan.