HotelHub HMSDocs

02 β€” Infrastructure #

Stack, deployment topology, scaling, monitoring, backup. Berlaku untuk standalone & SaaS.


1. Tech Stack #

Backend #

Frontend #

Library penting #

Optional #


2. Deployment Topology #

A. Standalone (single-tenant) β€” owner self-host #

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ VPS (Niagahoster, Biznet, AWS Lightsail, GCP)  β”‚
β”‚                                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                              β”‚
β”‚  β”‚   Nginx      β”‚  port 80, 443                β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                              β”‚
β”‚         β”‚                                      β”‚
β”‚         β–Ό                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                              β”‚
β”‚  β”‚  PHP-FPM 8.3 β”‚  Laravel 11                  β”‚
β”‚  β””β”€β”€β”¬β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                              β”‚
β”‚     β”‚   β”‚                                      β”‚
β”‚  β”Œβ”€β”€β–Όβ”€β” β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”‚
β”‚  β”‚MySQLβ”‚ └──▢ Redis (queue+cache+session)     β”‚
β”‚  β”‚  8  β”‚    β”‚  + Horizon worker (Supervisor)  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                              β”‚
β”‚  β”‚ Meilisearch  β”‚  port 7700 (localhost)       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                              β”‚
β”‚                                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                              β”‚
β”‚  β”‚  Local FS    β”‚  storage/app/                β”‚
β”‚  β”‚  atau S3-compat (MinIO / R2 / Wasabi)       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Sizing rekomendasi:

Tier Kamar vCPU RAM Disk Bandwidth
Lite ≀ 30 2 4 GB 60 GB SSD 2 TB/bln
Pro 30-100 4 8 GB 120 GB SSD 4 TB/bln
Enterprise > 100 8 16 GB 250 GB SSD 8 TB/bln

B. SaaS (multi-tenant) β€” kita host #

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Cloudflare (CDN + DDoS + SSL + WAF)                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Load Balancer (Nginx + ALB)                          β”‚
β””β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
   β”‚           β”‚           β”‚
β”Œβ”€β”€β–Όβ”€β”€β”€β”   β”Œβ”€β”€β–Όβ”€β”€β”€β”   β”Œβ”€β”€β–Όβ”€β”€β”€β”
β”‚ App1 β”‚   β”‚ App2 β”‚   β”‚ App3 β”‚  (Stateless Laravel)
β””β”€β”€β”¬β”€β”€β”€β”˜   β””β”€β”€β”¬β”€β”€β”€β”˜   β””β”€β”€β”¬β”€β”€β”€β”˜
   β”‚          β”‚          β”‚
   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
   β”‚          β”‚
β”Œβ”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ MySQL primary         β”‚    β”‚ Redis Clusterβ”‚
β”‚ + read replica        β”‚    β”‚ (cache, queue,β”‚
β”‚ + DB-per-tenant       β”‚    β”‚  session)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
   β”‚
β”Œβ”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ S3 / R2 (storage) β”‚    β”‚ Meilisearch HA  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Horizon worker pool x N    β”‚
β”‚ (queue: ota-sync, mail, ai)β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Region: Jakarta (AWS ap-southeast-3) atau Singapore (ap-southeast-1) untuk latency Indonesia <50ms.


3. Database Strategy #

Standalone #

SaaS #

Alternatif (kalau >1000 tenants):

Backup #


4. Queue Strategy #

Semua I/O berat masuk queue, bukan synchronous.

Job Queue Retry Timeout
OTA ARI sync (push) ota-sync 5x exponential 30s
OTA booking ingest ota-ingest 3x 10s
Send confirmation email mail 3x 30s
Send WhatsApp / SMS messaging 3x 15s
AI request (LLM) ai 2x 120s
pSEO content generation pseo 2x 120s
PDF invoice generation pdf 2x 30s
e-Faktur Coretax submit tax 5x 60s
Heartbeat license check default 1x 10s
Daily report email default 2x 60s

Worker:


5. Scheduler (cron) #

// app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
    // Night audit per property
    $schedule->command('hotel:night-audit')
        ->hourly()  // tiap property punya night audit time-nya sendiri, command cek
        ->withoutOverlapping();

    // OTA sync periodic (selain webhook)
    $schedule->command('ota:sync-availability')
        ->everyFifteenMinutes();

    // pSEO sitemap regenerate
    $schedule->command('pseo:generate-sitemap')
        ->dailyAt('03:00');

    // Daily flash report email
    $schedule->command('reports:daily-flash')
        ->dailyAt('08:00');

    // License heartbeat (standalone)
    $schedule->command('license:heartbeat')
        ->daily();

    // Backup
    $schedule->command('backup:run')
        ->dailyAt('02:00');

    // Clear old activity logs
    $schedule->command('activitylog:clean')
        ->weekly();

    // Refresh Coretax token
    $schedule->command('coretax:refresh-token')
        ->everyThirtyMinutes();

    // SaaS billing tick
    $schedule->command('billing:process')
        ->dailyAt('01:00')
        ->when(fn() => config('app.mode') === 'saas');

    // Index search engine
    $schedule->command('scout:reindex')
        ->dailyAt('04:00');
}

6. Caching Strategy #

Layer TTL Cache key pattern
Room availability search 60s avail:{property}:{start}:{end}:{adults}
Rate plan rules 5 min rate:{property}:{plan}:{date}
OTA inventory state 30s ota:{ota}:{property}:{date}
Guest profile (frequent lookup) 1 hour guest:{id}
Settings / config 1 day config:{property}
pSEO page render 6 hours pseo:{route_slug}
Sitemap chunk 24 hours sitemap:chunk:{n}
Audit log query (no cache) β€” append-only
License lock check 5 min license:lock (file mtime invalidated)

7. Storage #

Disk layout #

storage/app/
β”œβ”€β”€ public/
β”‚   β”œβ”€β”€ property/{id}/photos/
β”‚   β”œβ”€β”€ room/{id}/photos/
β”‚   β”œβ”€β”€ guest/{id}/{ktp,passport,signature}.jpg
β”‚   └── pseo-cache/
β”œβ”€β”€ private/
β”‚   β”œβ”€β”€ invoices/{year}/{month}/
β”‚   β”œβ”€β”€ e-faktur/{year}/{month}/
β”‚   β”œβ”€β”€ tax-reports/
β”‚   β”œβ”€β”€ lapor-imigrasi/
β”‚   β”œβ”€β”€ backups/
β”‚   └── audit-trails/
β”œβ”€β”€ llm-presets/
β”‚   β”œβ”€β”€ deepseek.json
β”‚   β”œβ”€β”€ gemini-flash.json
β”‚   β”œβ”€β”€ ... (lihat 05-AI_PROVIDERS)
└── ota-presets/
    β”œβ”€β”€ booking-com.json
    β”œβ”€β”€ agoda.json
    └── ...

Standalone disk #

SaaS disk #


8. SSL / Domain #

Standalone #

SaaS #


9. Email #


10. Monitoring & Observability #

Standalone (lightweight) #

SaaS (full) #

Health check JSON contract #

GET /health β†’ 200
{
  "status": "ok",
  "version": "1.0.0",
  "uptime_seconds": 38291,
  "checks": {
    "database": "ok",
    "redis": "ok",
    "storage": "ok",
    "queue_pending": 12,
    "queue_failed": 0,
    "license": "active"
  },
  "timestamp": "2026-04-28T10:00:00+07:00"
}

11. Security Hardening (infra layer) #


12. Backup & Disaster Recovery #

Standalone #

SaaS #


13. Deployment Pipeline #

Standalone (manual atau CD ringan) #

# Owner / kita push:
git pull origin release
composer install --no-dev --optimize-autoloader
npm ci && npm run build
php artisan down --retry=60
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
php artisan icons:cache
php artisan up
sudo supervisorctl restart hotel-worker:*

Future: php artisan app:update self-update command.

SaaS (full CI/CD) #

# .github/workflows/deploy.yml (sketch)
on:
  push:
    branches: [main]

jobs:
  deploy:
    steps:
      - checkout
      - php-test (PHPUnit + Pest)
      - phpstan analyse (level 6+)
      - laravel-pint (code style)
      - build-frontend (npm run build)
      - docker-build & push
      - deploy-blue-green (Kubernetes / ECS)
      - migrate-tenant-batched (rolling)
      - smoke-test
      - notify-slack

14. Development Environment #

# Local dev (Laragon Windows / Valet macOS / Sail Docker)
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate --seed
php artisan tinker  # data poking

# Frontend
npm install
npm run dev  # vite dev server with HMR

# Queue worker (terminal kedua)
php artisan queue:work --queue=ota-sync,mail,ai,default

# Scheduler test (terminal ketiga)
php artisan schedule:work

.env important keys:

APP_MODE=standalone              # standalone | saas
APP_LICENSE_DEV_BYPASS=true      # skip pairing wizard di local
DB_CONNECTION=mysql
QUEUE_CONNECTION=redis
CACHE_STORE=redis
SESSION_DRIVER=redis
BROADCAST_CONNECTION=null        # atau pusher di production realtime
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://localhost:7700

15. Performance Budget #

Metric Target
TTFB pSEO page < 500ms
TTFB admin dashboard < 800ms
Reservation create transaction < 1s
OTA sync push < 2s per OTA
AI concierge response < 3s
PDF invoice generation < 2s
Search room availability < 300ms
Booking engine search β†’ results < 1.5s

16. Capacity Planning #

Workload Per property estimate SaaS scaling
Reservations / day 5-50 Linear
OTA sync events / day 100-500 Queue-bound
POS transactions / day 50-500 Linear
Folio postings / day 100-1000 Linear
Active guests in DB 1k-50k Per-tenant DB scales independently
pSEO page hits / day 100-10k Heavily cached
AI requests / day 50-500 BYOK = no central bottleneck

17. Reference Specs (vendor) #

Vendor Product Note
Niagahoster Cloud VPS X1 (Rp 159rb/bln, 2 vCPU 2GB) Lite tier
Biznet Gio NEO Cloud VM Pro tier β€” region Indonesia
AWS Lightsail $40-80/bln Pro tier β€” region SG/Jakarta
GCP e2-standard-2 Sekitar $50/bln Pro tier
Cloudflare R2 $15/TB storage, $0 egress Storage SaaS β€” tercheap
Cloudflare Pages Static fallback Optional CDN booking widget
Mailgun / Resend $20-50/bln Email BYOK preset