diff --git a/.ghaymah.json b/.ghaymah.json
new file mode 100644
index 0000000..4ca67ff
--- /dev/null
+++ b/.ghaymah.json
@@ -0,0 +1,20 @@
+{
+ "id": "00000000-0000-0000-0000-000000000001",
+ "name": "nodeapp-production",
+ "projectId": "00000000-0000-0000-0000-000000000002",
+ "ports": [
+ {
+ "expose": true,
+ "number": 5000
+ }
+ ],
+ "publicAccess": {
+ "enabled": true,
+ "domain": "auto"
+ },
+ "resourceTier": "t1",
+ "container": {
+ "image": "docker.io/your-username/nodeapp:production",
+ "pullSecretName": ""
+ }
+}
diff --git a/metrics.json b/metrics.json
new file mode 100644
index 0000000..e412dbb
--- /dev/null
+++ b/metrics.json
@@ -0,0 +1,15 @@
+[
+ {
+ "timestamp": "2026-07-29T02:06:33.694428Z",
+ "latency_ms": 486.49,
+ "uptime": true,
+ "status_code": 200,
+ "dns_ms": 0.48,
+ "ssl_valid": false,
+ "ssl_expires": "Sep 15 13:10:47 2026 GMT",
+ "ssl_days_remaining": null,
+ "search_ms": 473.48,
+ "search_status_code": 200,
+ "overall": false
+ }
+]
\ No newline at end of file
diff --git a/q1-deploy-monitor/Dockerfile b/q1-deploy-monitor/Dockerfile
new file mode 100644
index 0000000..30882a1
--- /dev/null
+++ b/q1-deploy-monitor/Dockerfile
@@ -0,0 +1,6 @@
+FROM node:16.3.0-alpine
+COPY . /app
+RUN mkdir -p /logs && touch /logs/logs.txt
+WORKDIR /app
+RUN npm install
+CMD ["node", "/app/app.js"]
diff --git a/q1-deploy-monitor/app.js b/q1-deploy-monitor/app.js
new file mode 100644
index 0000000..b1a44e7
--- /dev/null
+++ b/q1-deploy-monitor/app.js
@@ -0,0 +1,82 @@
+const express = require('express')
+const fs = require('fs')
+const path = require('path')
+
+const app = express()
+const port = 5000
+const logDir = '/logs'
+const logFile = path.join(logDir, 'logs.txt')
+
+if (!fs.existsSync(logDir)) {
+ fs.mkdirSync(logDir, { recursive: true })
+}
+
+function getStatsFromLogFile() {
+ if (!fs.existsSync(logFile)) {
+ return { requestCount: 0, averageResponseTime: 0 }
+ }
+
+ const content = fs.readFileSync(logFile, 'utf8')
+ const lines = content.split('\n').filter(Boolean)
+ const requestCount = lines.length
+
+ if (requestCount === 0) {
+ return { requestCount: 0, averageResponseTime: 0 }
+ }
+
+ const totalResponseTime = lines.reduce((total, line) => {
+ const parts = line.trim().split(' ')
+ const duration = parseFloat(parts[1])
+ return total + (Number.isNaN(duration) ? 0 : duration)
+ }, 0)
+
+ return {
+ requestCount,
+ averageResponseTime: totalResponseTime / requestCount
+ }
+}
+
+app.use('/dashboard', express.static(path.join(__dirname, 'dashboard'), { index: false }))
+
+app.use((req, res, next) => {
+ const startTime = Date.now()
+ const requestTime = new Date(startTime).toISOString()
+
+ res.on('finish', () => {
+ const durationSeconds = (Date.now() - startTime) / 1000
+ const line = `${req.path} ${durationSeconds.toFixed(3)} ${requestTime} ${res.statusCode}\n`
+
+ fs.appendFile(logFile, line, (err) => {
+ if (err) console.error('Failed to write request log:', err)
+ })
+ })
+
+ next()
+})
+
+app.get('/dashboard', (req, res) => {
+ res.sendFile(path.join(__dirname, 'dashboard', 'index.html'))
+})
+
+app.get('/api/stats', (req, res) => {
+ const { requestCount, averageResponseTime } = getStatsFromLogFile()
+
+ res.json({
+ requestCount,
+ averageResponseTime,
+ status: 'up'
+ })
+})
+
+app.get('/', (req, res) => {
+ res.send('Hello World from GHAYMAH')
+})
+
+app.get('/health', (req, res) => {
+ res.send('The app up and runing')
+})
+
+app.listen(port, () => {
+ console.log(`Example app listening at http://localhost:${port}`)
+ console.log(`Dashboard available at http://localhost:${port}/dashboard`)
+})
diff --git a/q1-deploy-monitor/dashboard.js b/q1-deploy-monitor/dashboard.js
new file mode 100644
index 0000000..f3478d5
--- /dev/null
+++ b/q1-deploy-monitor/dashboard.js
@@ -0,0 +1,49 @@
+const requestCountEl = document.getElementById('requestCount')
+const averageTimeEl = document.getElementById('averageTime')
+const appStatusEl = document.getElementById('appStatus')
+const statusBadgeEl = document.getElementById('statusBadge')
+const statusTextEl = document.getElementById('statusText')
+const lastUpdatedEl = document.getElementById('lastUpdated')
+
+function setStatus(isUp) {
+ statusBadgeEl.classList.remove('up', 'down')
+ appStatusEl.classList.remove('up', 'down')
+
+ if (isUp) {
+ statusBadgeEl.classList.add('up')
+ appStatusEl.classList.add('up')
+ statusTextEl.textContent = 'Online'
+ appStatusEl.textContent = 'Up'
+ return
+ }
+
+ statusBadgeEl.classList.add('down')
+ appStatusEl.classList.add('down')
+ statusTextEl.textContent = 'Offline'
+ appStatusEl.textContent = 'Down'
+}
+
+async function fetchStats() {
+ try {
+ const response = await fetch('/api/stats')
+
+ if (!response.ok) {
+ throw new Error('Stats request failed')
+ }
+
+ const data = await response.json()
+
+ requestCountEl.textContent = data.requestCount.toLocaleString()
+ averageTimeEl.textContent = `${data.averageResponseTime.toFixed(3)}s`
+ setStatus(data.status === 'up')
+ lastUpdatedEl.textContent = `Last updated: ${new Date().toLocaleTimeString()}`
+ } catch (error) {
+ requestCountEl.textContent = '—'
+ averageTimeEl.textContent = '—'
+ setStatus(false)
+ lastUpdatedEl.textContent = 'Last updated: failed to connect'
+ }
+}
+
+fetchStats()
+setInterval(fetchStats, 5000)
diff --git a/q1-deploy-monitor/health-check.py b/q1-deploy-monitor/health-check.py
new file mode 100644
index 0000000..a0783d1
--- /dev/null
+++ b/q1-deploy-monitor/health-check.py
@@ -0,0 +1,13 @@
+from urllib.request import urlopen
+from urllib.error import HTTPError
+import time
+
+
+while True:
+ try:
+ with urlopen('https://my-application-2f85feb054d8.hosted.ghaymah.systems/health') as response:
+ print(f"Status Code: {response.status}")
+
+ except HTTPError as error:
+ print(f"Error Status Code: {error.code}")
+ time.sleep(30)
diff --git a/q1-deploy-monitor/index.html b/q1-deploy-monitor/index.html
new file mode 100644
index 0000000..0331e20
--- /dev/null
+++ b/q1-deploy-monitor/index.html
@@ -0,0 +1,50 @@
+
+
+
+
+
+ Server Dashboard
+
+
+
+
+
+
+
+
+ Total Requests
+ —
+ All requests received by the server
+
+
+
+ Average Response Time
+ —
+ Mean duration in seconds
+
+
+
+ Application Status
+ —
+ Based on health check
+
+
+
+
+
+
+
+
+
diff --git a/q1-deploy-monitor/package.json b/q1-deploy-monitor/package.json
new file mode 100644
index 0000000..a7719cf
--- /dev/null
+++ b/q1-deploy-monitor/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "nodeapp",
+ "version": "1.0.0",
+ "description": "",
+ "main": "app.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "express": "^4.17.1"
+ },
+ "devDependencies": {
+ "nodemon": "^3.1.14"
+ }
+}
diff --git a/q1-deploy-monitor/style.css b/q1-deploy-monitor/style.css
new file mode 100644
index 0000000..496da72
--- /dev/null
+++ b/q1-deploy-monitor/style.css
@@ -0,0 +1,158 @@
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
+ background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%);
+ min-height: 100vh;
+ color: #e2e8f0;
+ padding: 2rem;
+}
+
+.dashboard {
+ max-width: 1100px;
+ margin: 0 auto;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1.5rem;
+ margin-bottom: 2rem;
+ flex-wrap: wrap;
+}
+
+.header h1 {
+ font-size: 2rem;
+ font-weight: 700;
+ color: #f8fafc;
+}
+
+.subtitle {
+ margin-top: 0.35rem;
+ color: #94a3b8;
+ font-size: 0.95rem;
+}
+
+.status-badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.6rem;
+ padding: 0.65rem 1.1rem;
+ border-radius: 999px;
+ background: rgba(15, 23, 42, 0.7);
+ border: 1px solid rgba(148, 163, 184, 0.25);
+ font-weight: 600;
+}
+
+.status-dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ background: #64748b;
+ box-shadow: 0 0 0 0 rgba(100, 116, 139, 0.5);
+}
+
+.status-badge.up .status-dot {
+ background: #22c55e;
+ animation: pulse 2s infinite;
+}
+
+.status-badge.down .status-dot {
+ background: #ef4444;
+}
+
+.status-badge.up {
+ border-color: rgba(34, 197, 94, 0.35);
+ color: #bbf7d0;
+}
+
+.status-badge.down {
+ border-color: rgba(239, 68, 68, 0.35);
+ color: #fecaca;
+}
+
+.cards {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+ gap: 1.25rem;
+}
+
+.card {
+ background: rgba(30, 41, 59, 0.85);
+ border: 1px solid rgba(148, 163, 184, 0.15);
+ border-radius: 16px;
+ padding: 1.5rem;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
+ transition: transform 0.2s ease, border-color 0.2s ease;
+}
+
+.card:hover {
+ transform: translateY(-2px);
+ border-color: rgba(96, 165, 250, 0.35);
+}
+
+.card-label {
+ color: #94a3b8;
+ font-size: 0.85rem;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ margin-bottom: 0.75rem;
+}
+
+.card-value {
+ font-size: 2.4rem;
+ font-weight: 700;
+ color: #f8fafc;
+ line-height: 1.1;
+}
+
+.status-value.up {
+ color: #4ade80;
+}
+
+.status-value.down {
+ color: #f87171;
+}
+
+.card-hint {
+ margin-top: 0.75rem;
+ color: #64748b;
+ font-size: 0.85rem;
+}
+
+.footer {
+ margin-top: 2rem;
+ display: flex;
+ justify-content: space-between;
+ gap: 1rem;
+ flex-wrap: wrap;
+ color: #64748b;
+ font-size: 0.85rem;
+}
+
+@keyframes pulse {
+ 0% {
+ box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.5);
+ }
+ 70% {
+ box-shadow: 0 0 0 8px rgba(34, 197, 94, 0);
+ }
+ 100% {
+ box-shadow: 0 0 0 0 rgba(34, 197, 94, 0);
+ }
+}
+
+@media (max-width: 600px) {
+ body {
+ padding: 1rem;
+ }
+
+ .card-value {
+ font-size: 2rem;
+ }
+}
diff --git a/q2-postmortem/postmortem-report.md b/q2-postmortem/postmortem-report.md
new file mode 100644
index 0000000..e69de29
diff --git a/q3-cicd/workflow.yml b/q3-cicd/workflow.yml
new file mode 100644
index 0000000..58f8b73
--- /dev/null
+++ b/q3-cicd/workflow.yml
@@ -0,0 +1,175 @@
+name: CI/CD - Ghaymah Cloud
+
+on:
+ push:
+ branches: [main, staging]
+ pull_request:
+ branches: [main, staging]
+
+env:
+ DOCKERHUB_REPOSITORY: ${{ secrets.DOCKERHUB_REPOSITORY }}
+
+jobs:
+ build-and-push:
+ name: Build and Push to Docker Hub
+ runs-on: ubuntu-latest
+ if: github.event_name == 'push'
+ outputs:
+ environment: ${{ steps.env.outputs.environment }}
+ image_uri: ${{ steps.image.outputs.image_uri }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5.0.0
+
+
+ - name: Set deployment environment
+ id: env
+ run: |
+ if [ "${{ github.ref_name }}" = "main" ]; then
+ echo "environment=production" >> $GITHUB_OUTPUT
+ else
+ echo "environment=staging" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Login to Docker Hub
+ uses: docker/login-action@v3.6.0
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Build and push Docker image
+ uses: docker/build-push-action@v6
+ with:
+ context: ./nodeapp
+ push: true
+ tags: |
+ docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPOSITORY }}:production
+ docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPOSITORY }}:staging
+ docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPOSITORY }}:${{ github.sha }}
+
+ - name: Set deployment image
+ id: image
+ run: |
+ IMAGE_BASE="docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPOSITORY }}"
+ if [ "${{ steps.env.outputs.environment }}" = "production" ]; then
+ echo "image_uri=${IMAGE_BASE}:production" >> $GITHUB_OUTPUT
+ else
+ echo "image_uri=${IMAGE_BASE}:staging" >> $GITHUB_OUTPUT
+ fi
+
+ build:
+ name: Build Docker Image
+ runs-on: ubuntu-latest
+ if: github.event_name == 'pull_request'
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5.0.0
+
+ - name: Build Docker image
+ run: docker build ./nodeapp
+
+ deploy-staging:
+ name: Deploy to Staging
+ runs-on: ubuntu-latest
+ needs: build-and-push
+ if: github.event_name == 'push' && github.ref_name == 'staging'
+ environment:
+ name: staging
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5.0.0
+
+ - name: Install Ghaymah CLI
+ run: curl -sSL https://cli.ghaymah.systems/install.sh | bash
+
+ - name: Login to Ghaymah
+ run: $HOME/ghaymah/bin/gy auth login --email "${{ secrets.GHAYMAH_EMAIL }}" --password "${{ secrets.GHAYMAH_PW }}"
+
+ - name: Create Ghaymah configuration
+ working-directory: nodeapp
+ env:
+ IMAGE_URI: ${{ needs.build-and-push.outputs.image_uri }}
+ PROJECT_ID: ${{ secrets.GHAYMAH_PROJECT_ID }}
+ APP_ID: ${{ secrets.GHAYMAH_APP_ID_STAGING }}
+ PULL_SECRET: ${{ secrets.DOCKERHUB_PULL_SECRET_NAME }}
+ run: |
+ jq -n \
+ --arg id "${APP_ID}" \
+ --arg name "nodeapp-staging" \
+ --arg projectId "${PROJECT_ID}" \
+ --arg image "${IMAGE_URI}" \
+ --arg pullSecret "${PULL_SECRET}" \
+ '{
+ id: $id,
+ name: $name,
+ projectId: $projectId,
+ ports: [{ expose: true, number: 5000 }],
+ publicAccess: {
+ enabled: true,
+ domain: "auto"
+ },
+ resourceTier: "t1",
+ container: {
+ image: $image,
+ pullSecretName: $pullSecret
+ }
+ }' > .ghaymah.json
+
+ cat .ghaymah.json
+
+ - name: Deploy to Ghaymah
+ working-directory: nodeapp
+ run: $HOME/ghaymah/bin/gy resource app launch --env staging
+
+ deploy-production:
+ name: Deploy to Production
+ runs-on: ubuntu-latest
+ needs: build-and-push
+ if: github.event_name == 'push' && github.ref_name == 'main'
+ environment:
+ name: production
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5.0.0
+
+ - name: Install Ghaymah CLI
+ run: curl -sSL https://cli.ghaymah.systems/install.sh | bash
+
+ - name: Login to Ghaymah
+ run: $HOME/ghaymah/bin/gy auth login --email "${{ secrets.GHAYMAH_EMAIL }}" --password "${{ secrets.GHAYMAH_PW }}"
+
+ - name: Create Ghaymah configuration
+ working-directory: nodeapp
+ env:
+ IMAGE_URI: ${{ needs.build-and-push.outputs.image_uri }}
+ PROJECT_ID: ${{ secrets.GHAYMAH_PROJECT_ID }}
+ APP_ID: ${{ secrets.GHAYMAH_APP_ID_PRODUCTION }}
+ PULL_SECRET: ${{ secrets.DOCKERHUB_PULL_SECRET_NAME }}
+ run: |
+ jq -n \
+ --arg id "${APP_ID}" \
+ --arg name "nodeapp-production" \
+ --arg projectId "${PROJECT_ID}" \
+ --arg image "${IMAGE_URI}" \
+ --arg pullSecret "${PULL_SECRET}" \
+ '{
+ id: $id,
+ name: $name,
+ projectId: $projectId,
+ ports: [{ expose: true, number: 5000 }],
+ publicAccess: {
+ enabled: true,
+ domain: "auto"
+ },
+ resourceTier: "t1",
+ container: {
+ image: $image,
+ pullSecretName: $pullSecret
+ }
+ }' > .ghaymah.json
+
+ cat .ghaymah.json
+
+ - name: Deploy to Ghaymah
+ working-directory: nodeapp
+ run: $HOME/ghaymah/bin/gy resource app launch --env production
diff --git a/q4-scalability/architecture.png b/q4-scalability/architecture.png
new file mode 100644
index 0000000..6ca1ebb
Binary files /dev/null and b/q4-scalability/architecture.png differ
diff --git a/q4-scalability/calculations.md b/q4-scalability/calculations.md
new file mode 100644
index 0000000..2e5cf76
--- /dev/null
+++ b/q4-scalability/calculations.md
@@ -0,0 +1,170 @@
+# Q4 — قابلية التوسع وتوزيع الأحمال
+
+## 1. Architecture Diagram
+
+راجع الملف `architecture.png` في هذا المجلد.
+
+يوضّح المخطط تدفّق **15,000 req/s** عبر:
+
+| الطبقة | المكوّن | الدور |
+|--------|---------|-------|
+| Edge | Ghaymah Load Balancer (L7) | توزيع الطلبات، SSL، Health Checks |
+| Compute | Auto Scaling Group — 43 حاوية | معالجة الطلبات (500 req/s لكل حاوية) |
+| Stateful | Ghaymah Block Storage | تخزين دائم للـ stateful workloads |
+| Cache/CDN | Object Storage / CDN | الملفات الثابتة والوسائط |
+| Ops | Monitoring & Alerts | مقاييس، تنبيهات، قرارات التوسع |
+| Warm Pool | Cold Start Pool | حاويات مُسخَّنة مسبقاً لتقليل زمن الإقلاع |
+
+---
+
+## 2. حساب عدد الحاويات
+
+### المعطيات
+
+| المتغير | القيمة |
+|---------|--------|
+| إجمالي الحمل | **15,000 req/s** |
+| سعة الحاوية الواحدة | **500 req/s** |
+| هامش الأمان | **30%** |
+
+### المنطق
+
+الهامش 30% يعني **عدم تشغيل الحاويات فوق 70%** من سعتها القصوى، لترك مساحة للذروات والتقلبات:
+
+```
+السعة الفعلية القابلة للتخطيط = 500 × (1 − 0.30) = 500 × 0.70 = 350 req/s لكل حاوية
+```
+
+### الحساب
+
+```
+عدد الحاويات = ⌈ 15,000 ÷ 350 ⌉
+ = ⌈ 42.857 ⌉
+ = 43 حاوية
+```
+
+### التحقق
+
+```
+43 × 350 = 15,050 req/s ✅ (يغطي 15,000 req/s مع هامش 30%)
+```
+
+### ملخص
+
+| البند | القيمة |
+|-------|--------|
+| الحد الأدنى نظرياً (بدون هامش) | 30 حاوية |
+| **العدد الموصى به (مع هامش 30%)** | **43 حاوية** |
+| السعة الإجمالية الفعلية | 15,050 req/s |
+| نسبة الاستخدام عند 15K req/s | ≈ 99.7% من السعة المُخطَّطة |
+
+> **ملاحظة:** إذا قصدت إضافة 30% فوق العدد الأساسي: `30 × 1.30 = 39 حاوية`.
+> في سياق SRE، هامش السعة (capacity headroom) هو التفسير الأدق — أي 43 حاوية.
+
+---
+
+## 3. استراتيجية Cold Start للحاويات الجديدة
+
+عند التوسع الأفقي (scale-out)، الحاوية الجديدة تحتاج وقتاً قبل أن تستقبل حركة. الاستراتيجية المقترحة:
+
+### أ) Warm Pool (مجموعة تسخين)
+
+- الإبقاء على **3–5 حاويات جاهزة** في حالة `standby` (مُشغَّلة ومُسخَّنة، لكن خارج rotation الـ Load Balancer).
+- عند ارتفاع الحمل، تُضاف فوراً إلى الـ LB **بدون** انتظار pull للصورة أو boot.
+
+### ب) Pre-pull الصور
+
+- تخزين صورة Docker في **Ghaymah Internal Registry** داخل نفس المنطقة.
+- تقليل زمن `image pull` من دقائق إلى ثوانٍ.
+
+### ج) Readiness Probe تدريجي
+
+```
+Startup Probe → Readiness Probe → إضافة للـ LB
+ (30s) (HTTP /health)
+```
+
+- لا تُوجَّه الطلبات للحاوية حتى تمر `/health` بنجاح.
+- يمنع إرسال حركة لحاوية لم تكتمل تهيئتها.
+
+### د) Graceful Scale-In
+
+- عند التقليص: إرسال `SIGTERM` → انتظار إنهاء الطلبات الجارية (drain) → إزالة من LB → إيقاف.
+- يمنع قطع الطلبات أثناء التوسع العكسي.
+
+### هـ) Predictive Scaling
+
+- مراقبة CPU / RPS / latency.
+- بدء التوسع **قبل** الوصول للحد (مثلاً عند 60% utilization) وليس عند 90%.
+
+### ف) Application Warm-up
+
+- عند الإقلاع: تحميل cache محلي، pre-connect لقاعدة البيانات، JIT warm-up.
+- endpoint `/warmup` يُستدعى تلقائياً بعد readiness.
+
+---
+
+## 4. Ghaymah Block Storage للـ Stateful Workloads
+
+### ما هو Block Storage؟
+
+تخزين **كتلة (block-level)** يُ attach كقرص افتراضي (volume) لحاوية أو خدمة — مثل `/dev/vdb` — ويحتفظ بالبيانات **بعد** إعادة تشغيل أو استبدال الحاوية.
+
+### متى نستخدمه؟
+
+| Stateful | Stateless (لا يحتاج Block Storage) |
+|----------|-------------------------------------|
+| PostgreSQL / MySQL | API servers (43 حاوية أعلاه) |
+| Redis persistence (AOF/RDB) | Static assets → Object Storage |
+| File uploads / media processing | Session في Redis مشترك |
+| Logs طويلة الأمد | |
+
+### كيف يُستخدم على غيمة؟
+
+1. **إنشاء Volume**
+ - حجم مناسب (مثلاً 100 GB SSD) في نفس منطقة التطبيق.
+
+2. **Mount على الحاوية Stateful**
+ ```text
+ /data/postgresql ← Ghaymah Block Volume (persistent)
+ ```
+
+3. **فصل Compute عن Storage**
+ - حاويات الـ API **stateless** — تتوسع أفقياً بحرية.
+ - قاعدة البيانات **stateful** — volume واحد (أو cluster مع replication).
+
+4. **Snapshots & Backup**
+ - Ghaymah Backup: snapshots دورية للـ volume.
+ - استرداد Point-in-Time عند الفشل.
+
+5. **High Availability**
+ - Primary DB على volume + Replica على volume منفصل.
+ - Failover تلقائي عند سقوط Primary.
+
+### في مخططنا (architecture.png)
+
+```
+Load Balancer → 43 App Containers (stateless)
+ ↓
+ Stateful DB Pod ← Ghaymah Block Storage (persistent volume)
+```
+
+- الـ **43 حاوية** لا تخزّن state محلياً.
+- كل البيانات الدائمة على **Block Storage** م attached لطبقة DB/Cache الم persistent.
+
+### الفوائد
+
+| الفائدة | الشرح |
+|---------|--------|
+| Persistence | البيانات تبقى بعد restart/redeploy |
+| Performance | IOPS مضمون لقواعد البيانات |
+| Scalability | فصل التوسع الأفقي (API) عن التخزين (DB) |
+| DR | Snapshots + restore سريع |
+
+---
+
+## المراجع
+
+- [Ghaymah Cloud](https://ghaymah.systems/)
+- [Ghaymah CLI — `gy resource app launch`](https://cli.ghaymah.systems/)
+- Health & Retry: HTTP/TCP probes — down-nodes تخرج فوراً من rotation