Kamal 2: نشر تطبيق Next.js على VPS بدون توقف

مقدمة
نشر تطبيق ويب في بيئة الإنتاج لا يجب أن يتطلب مجموعة Kubernetes أو مهندس DevOps مخصص أو فاتورة سحابية بأرقام كبيرة. ومع ذلك، في عام 2026، يجد العديد من المطورين أنفسهم محاصرين بين نقيضين: المنصات المُدارة المكلفة (Vercel، Railway) وتعقيد Kubernetes.
Kamal 2 هو إجابة 37signals (مبتكرو Ruby on Rails و Basecamp) على هذه المعضلة. كان يُعرف سابقاً باسم MRSK، وKamal هو أداة نشر مفتوحة المصدر تتيح لك نشر التطبيقات المُحاوية على أي خادم — VPS أو bare metal أو سحابي — مع صفر توقف وتراجع فوري وSSL تلقائي عبر Traefik.
ما يجعل Kamal 2 مميزاً بشكل خاص:
- لا قيود على المزود — يعمل على أي خادم يدعم SSH و Docker
- صفر توقف — يتم تشغيل الإصدارات الجديدة قبل إيقاف القديمة
- تكوين بسيط — ملف YAML واحد (
config/deploy.yml) - تراجع بأمر واحد — العودة للإصدار السابق فوراً
- متعدد الخوادم — النشر على عدة أجهزة بالتوازي
- دعم الملحقات — إدارة قواعد البيانات والخدمات المساعدة
في هذا الدليل التعليمي، ستتعلم:
- تثبيت وتكوين Kamal 2 على جهازك المحلي
- إعداد VPS للنشر
- حاوية تطبيق Next.js باستخدام Dockerfile محسّن
- تكوين Kamal للنشر بدون توقف
- إعداد SSL تلقائي مع Traefik
- النشر والمراقبة والتراجع
المتطلبات الأساسية
قبل البدء، تأكد من توفر:
- خادم VPS بذاكرة 1 جيجابايت على الأقل و 1 vCPU (DigitalOcean، Hetzner، OVH، Contabo، إلخ.)
- Ubuntu 22.04 أو 24.04 مثبت على الخادم
- اسم نطاق يشير إلى خادمك (سجل DNS A)
- Docker مثبت محلياً (لبناء الصور)
- Ruby 3.1+ مثبت محلياً (Kamal مكتوب بـ Ruby)
- تطبيق Next.js جاهز للنشر
- حساب Docker Hub (أو سجل حاويات آخر)
- معرفة أساسية بـ سطر أوامر Linux و Docker
ما ستبنيه
بنهاية هذا الدليل، سيكون لديك بنية نشر كاملة:
- خادم VPS مُعَد تلقائياً بواسطة Kamal
- تطبيق Next.js في بيئة الإنتاج مع SSL
- وكيل Traefik يدير التوجيه والشهادات
- خط أنابيب نشر بدون توقف
- القدرة على التراجع بأمر واحد
الخطوة 1: تثبيت Kamal 2
يتم توزيع Kamal كحزمة Ruby gem. قم بتثبيته عالمياً على جهاز التطوير:
gem install kamalتحقق من أن Kamal مثبت بشكل صحيح:
kamal versionيجب أن ترى شيئاً مثل 2.4.0 أو إصدار أحدث.
إذا كنت تفضل عدم تثبيت Ruby، يمكنك استخدام Kamal عبر Docker:
alias kamal='docker run -it --rm -v "${PWD}:/workdir" -v "/run/host-services/ssh-auth.sock:/run/host-services/ssh-auth.sock" -e SSH_AUTH_SOCK="/run/host-services/ssh-auth.sock" -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/basecamp/kamal:latest'الخطوة 2: إعداد الخادم VPS
2.1 إنشاء الخادم
أنشئ خادم VPS من مزودك المفضل. لهذا الدليل، خادم بهذه المواصفات الدنيا يكفي:
| المورد | الحد الأدنى | الموصى به |
|---|---|---|
| الذاكرة | 1 جيجابايت | 2 جيجابايت |
| المعالج | 1 vCPU | 2 vCPUs |
| التخزين | 20 جيجابايت SSD | 40 جيجابايت SSD |
| النظام | Ubuntu 22.04 | Ubuntu 24.04 |
2.2 تكوين DNS
وجّه نطاقك إلى الخادم:
Type: A
Name: app (أو @ للجذر)
Value: <عنوان_IP_خادمك>
TTL: 300
2.3 تكوين SSH
تأكد من أنه يمكنك الاتصال بالخادم عبر SSH باستخدام مفتاحك:
ssh root@your-server.comيتصل Kamal عبر SSH لتكوين Docker ونشر الحاويات. يتولى تلقائياً تثبيت Docker على الخادم أثناء النشر الأول — لا حاجة لتثبيت أي شيء يدوياً.
الخطوة 3: إعداد تطبيق Next.js
3.1 إنشاء المشروع (إذا لزم الأمر)
إذا كنت تبدأ من الصفر:
npx create-next-app@latest my-kamal-app --typescript --tailwind --app
cd my-kamal-app3.2 إنشاء Dockerfile
أنشئ Dockerfile محسّن للإنتاج في جذر المشروع:
# المرحلة 1: تثبيت التبعيات
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# المرحلة 2: بناء التطبيق
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
RUN npm run build
# المرحلة 3: صورة الإنتاج
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]3.3 تكوين Next.js لوضع Standalone
عدّل ملف next.config.js (أو next.config.ts) لتفعيل وضع standalone:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
}
module.exports = nextConfigوضع standalone يولّد خادم Node.js مستقل يتضمن فقط الملفات الضرورية، مما يقلل حجم صورة Docker بشكل كبير.
3.4 إضافة ملف .dockerignore
أنشئ ملف .dockerignore لتسريع عمليات البناء:
node_modules
.next
.git
.gitignore
*.md
docker-compose*.yml
.env*.local
الخطوة 4: تهيئة Kamal
4.1 توليد التكوين
من جذر مشروع Next.js:
kamal initينشئ هذا الأمر الملفات التالية:
config/
deploy.yml # التكوين الرئيسي
.kamal/
secrets # متغيرات البيئة السرية
.env # متغيرات محلية (تُضاف إلى .gitignore)
4.2 تكوين deploy.yml
استبدل محتوى config/deploy.yml بـ:
service: my-nextjs-app
image: your-docker-hub-user/my-nextjs-app
servers:
web:
hosts:
- your-server.com
labels:
traefik.http.routers.my-nextjs-app.rule: Host(`app.your-domain.com`)
traefik.http.routers.my-nextjs-app.entrypoints: websecure
traefik.http.routers.my-nextjs-app.tls.certresolver: letsencrypt
options:
network: kamal
proxy:
ssl: true
host: app.your-domain.com
registry:
username: your-docker-hub-user
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
NODE_ENV: production
HOSTNAME: 0.0.0.0
secret:
- DATABASE_URL
builder:
dockerfile: Dockerfile
multiarch: false
healthcheck:
path: /api/health
port: 3000
interval: 10
max_attempts: 30لنحلل الأقسام المهمة:
- service: اسم تطبيقك (يُستخدم لتسمية الحاويات)
- image: المسار الكامل لصورة Docker على السجل
- servers: قائمة الخوادم المستهدفة مع تسميات Traefik للتوجيه
- proxy: يفعّل SSL التلقائي عبر Traefik و kamal-proxy
- registry: بيانات اعتماد سجل Docker
- env: متغيرات البيئة (واضحة وسرية منفصلة)
- healthcheck: يتحقق Kamal من استجابة تطبيقك قبل تبديل حركة المرور
4.3 تكوين الأسرار
حرّر ملف .kamal/secrets:
KAMAL_REGISTRY_PASSWORD=your-docker-hub-password
DATABASE_URL=postgresql://user:pass@db-host:5432/mydbيجب ألا يُلتزم هذا الملف أبداً في Git. تحقق من أن .kamal/secrets موجود في .gitignore.
4.4 إنشاء نقطة فحص الصحة
يستخدم Kamal فحص الصحة للتحقق من جاهزية تطبيقك قبل تبديل حركة المرور. أنشئ نقطة نهاية API بسيطة:
// app/api/health/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
return NextResponse.json({
status: 'ok',
timestamp: new Date().toISOString(),
})
}الخطوة 5: فهم النشر بدون توقف
قبل إطلاق أول نشر، دعنا نفهم كيف يضمن Kamal 2 عدم التوقف:
تدفق النشر
1. بناء صورة Docker محلياً (أو في CI)
2. دفع الصورة إلى السجل
3. سحب الصورة على الخادم(الخوادم)
4. تشغيل الحاوية الجديدة
5. فحص الصحة: Kamal ينتظر استجابة /api/health بـ 200
6. kamal-proxy يبدّل حركة المرور إلى الحاوية الجديدة
7. إيقاف الحاوية القديمة بشكل أنيق
النقطة الأساسية هي الخطوة 6: حركة المرور تتبدّل فقط بعد نجاح الحاوية الجديدة في فحص الصحة. هذا يعني أن مستخدميك لن يروا أبداً خطأ 502 أو صفحة فارغة أثناء النشر.
kamal-proxy مقابل Traefik
يستخدم Kamal 2 kamal-proxy، وكيل عكسي خفيف تم تطويره خصيصاً لـ Kamal. يحل محل الاعتماد على Traefik للتوجيه الأساسي، رغم أن Traefik يبقى متاحاً كخيار للتكوينات المتقدمة (خدمات متعددة، وسيط، إلخ.).
يتولى kamal-proxy:
- تبديل حركة المرور بين الحاوية القديمة والجديدة
- إنهاء SSL مع Let's Encrypt
- فحوصات الصحة التلقائية
- تصريف الاتصالات الحالية
الخطوة 6: النشر الأول
6.1 تشغيل الإعداد الأولي
النشر الأول يتطلب خطوة إعداد تُكوّن Docker و kamal-proxy على الخادم:
kamal setupينفذ هذا الأمر العمليات التالية:
- الاتصال بالخادم عبر SSH
- تثبيت Docker (إذا لم يكن موجوداً)
- تثبيت kamal-proxy
- تسجيل الدخول إلى سجل Docker
- بناء ودفع الصورة
- سحب وتشغيل الحاوية
- تكوين SSL
سترى مخرجات تفصيلية لكل خطوة:
INFO [f97da026] Running docker login ...
INFO [f97da026] Finished in 1.337 seconds
INFO Building image ...
INFO Pushing image ...
INFO [48716498] Running docker pull ...
INFO [48716498] Finished in 8.234 seconds
INFO [48716498] Running docker run ...
INFO Waiting for healthy container ...
INFO Container is healthy!
6.2 التحقق من النشر
بعد الانتهاء، تحقق من أن كل شيء يعمل:
# التحقق من الحاويات قيد التشغيل
kamal details
# عرض سجلات التطبيق
kamal app logs
# التحقق من حالة الوكيل
kamal proxy detailsزُر https://app.your-domain.com — يجب أن يكون تطبيق Next.js متاحاً مع شهادة SSL صالحة.
الخطوة 7: عمليات النشر اللاحقة
لعمليات النشر اللاحقة، أمر واحد يكفي:
kamal deployهذا كل شيء. سيقوم Kamal بـ:
- بناء الصورة الجديدة
- دفعها إلى السجل
- سحبها على الخادم
- تشغيل الحاوية الجديدة
- التحقق من فحص الصحة
- تبديل حركة المرور
- إيقاف الحاوية القديمة
النشر بدون إعادة بناء الصورة
إذا كنت قد بنيت ودفعت الصورة مسبقاً (مثلاً في CI):
kamal deploy --skip-pushالخطوة 8: التراجع
أحد أكبر مزايا Kamal هو سهولة التراجع. إذا سبب النشر مشاكل:
# التراجع إلى إصدار سابق
kamal rollback [TAG_الإصدار]لرؤية الإصدارات المتاحة:
kamal app containersالتراجع شبه فوري لأن الصورة السابقة موجودة بالفعل على الخادم. يعيد Kamal ببساطة تشغيل الحاوية القديمة ويبدّل حركة المرور.
الخطوة 9: إدارة الملحقات (قواعد البيانات، Redis، إلخ.)
يمكن لـ Kamal إدارة خدمات مساعدة تسمى accessories. أضفها في config/deploy.yml:
accessories:
db:
image: postgres:16-alpine
host: your-server.com
port: "5432:5432"
env:
clear:
POSTGRES_DB: my_app
secret:
- POSTGRES_PASSWORD
directories:
- data:/var/lib/postgresql/data
options:
network: kamal
redis:
image: redis:7-alpine
host: your-server.com
port: "6379:6379"
directories:
- data:/data
options:
network: kamalنشر الملحقات:
# نشر جميع الملحقات
kamal accessory boot all
# أو ملحق محدد
kamal accessory boot dbالملحقات دائمة — لا يتم إعادة تشغيلها أثناء نشر التطبيق الرئيسي.
الخطوة 10: النشر متعدد الخوادم
للتطبيقات ذات الحركة العالية، يمكنك النشر على عدة خوادم:
servers:
web:
hosts:
- server-1.your-domain.com
- server-2.your-domain.com
- server-3.your-domain.comينشر Kamal على جميع الخوادم بالتوازي. مع موازن أحمال (مثل DigitalOcean Load Balancer أو DNS round-robin)، تحصل على توفر عالٍ بدون جهد إضافي.
أدوار الخادم
يمكنك تحديد أدوار مختلفة لخوادمك:
servers:
web:
hosts:
- web-1.example.com
- web-2.example.com
worker:
hosts:
- worker-1.example.com
cmd: npm run workerالخطوة 11: التكامل مع CI/CD باستخدام GitHub Actions
أتمت النشر باستخدام GitHub Actions:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
concurrency:
group: deploy
cancel-in-progress: true
env:
DOCKER_BUILDKIT: 1
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.3"
bundler-cache: true
- name: Install Kamal
run: gem install kamal
- name: Set up SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Deploy with Kamal
env:
KAMAL_REGISTRY_PASSWORD: ${{ secrets.DOCKER_HUB_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: kamal deployأضف الأسرار التالية في مستودع GitHub:
SSH_PRIVATE_KEY: مفتاح SSH الخاص بخادمكDOCKER_HUB_TOKEN: رمز Docker HubDATABASE_URL: سلسلة اتصال قاعدة البيانات
الخطوة 12: الأوامر اليومية
إليك أوامر Kamal التي ستستخدمها أكثر:
إدارة التطبيق
# النشر
kamal deploy
# عرض السجلات مباشرة
kamal app logs -f
# تنفيذ أمر في الحاوية
kamal app exec "node -e 'console.log(process.env.NODE_ENV)'"
# فتح جلسة تفاعلية
kamal app exec -i bash
# إعادة تشغيل التطبيق
kamal app boot
# إيقاف التطبيق
kamal app stopإدارة الوكيل
# عرض تكوين الوكيل
kamal proxy details
# إعادة تحميل التكوين
kamal proxy rebootإدارة الملحقات
# عرض سجلات قاعدة البيانات
kamal accessory logs db
# إعادة تشغيل Redis
kamal accessory reboot redis
# تشغيل psql
kamal accessory exec db "psql -U postgres my_app"الصيانة
# تنظيف صور Docker القديمة
kamal prune all
# قفل النشر (صيانة)
kamal lock acquire -m "صيانة جارية"
# فتح القفل
kamal lock releaseالخطوة 13: مراقبة تطبيقك
فحص صحة متقدم
حسّن نقطة فحص الصحة للتحقق من التبعيات:
// app/api/health/route.ts
import { NextResponse } from 'next/server'
interface HealthStatus {
status: string
timestamp: string
uptime: number
checks: {
database?: string
memory?: {
used: number
total: number
}
}
}
export async function GET() {
const health: HealthStatus = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
checks: {},
}
// التحقق من الذاكرة
const memUsage = process.memoryUsage()
health.checks.memory = {
used: Math.round(memUsage.heapUsed / 1024 / 1024),
total: Math.round(memUsage.heapTotal / 1024 / 1024),
}
return NextResponse.json(health)
}المراقبة الخارجية
لمراقبة شاملة، يمكنك إضافة خدمة مراقبة كملحق:
accessories:
uptime-kuma:
image: louislam/uptime-kuma:1
host: your-server.com
port: "3001:3001"
directories:
- data:/app/data
options:
network: kamalاستكشاف الأخطاء
فشل فحص الصحة
إذا فشل النشر بسبب انتهاء مهلة فحص الصحة:
-
تحقق من سجلات الحاوية:
kamal app logs -
اختبر فحص الصحة محلياً:
docker build -t test-app . docker run -p 3000:3000 test-app curl http://localhost:3000/api/health -
زد المهلة في
deploy.yml:healthcheck: max_attempts: 60 interval: 5
خطأ صلاحية SSH
إذا لم يتمكن Kamal من الاتصال بالخادم:
# تحقق من تحميل مفتاحك
ssh-add -l
# أضف مفتاحك إذا لزم الأمر
ssh-add ~/.ssh/id_ed25519
# اختبر الاتصال
ssh root@your-server.com "echo OK"الصورة كبيرة جداً
إذا استغرق البناء أو الدفع وقتاً طويلاً، حسّن Dockerfile:
- استخدم البناء متعدد المراحل (تم بالفعل في Dockerfile الخاص بنا)
- تحقق من
.dockerignore - استخدم
node:20-alpineبدلاً منnode:20 - فعّل BuildKit لتخزين الطبقات:
export DOCKER_BUILDKIT=1
مشكلة شهادة SSL
إذا لم يعمل SSL:
-
تحقق من أن DNS يشير إلى الخادم:
dig app.your-domain.com -
تحقق من سجلات الوكيل:
kamal proxy logs -
تأكد من فتح المنافذ 80 و 443 على جدار حماية الخادم.
Kamal مقابل البدائل
| الميزة | Kamal 2 | Coolify | Vercel | Kubernetes |
|---|---|---|---|---|
| صفر توقف | نعم | نعم | نعم | نعم |
| SSL تلقائي | نعم | نعم | نعم | معقد |
| تراجع | أمر واحد | واجهة | تلقائي | معقد |
| متعدد الخوادم | نعم | نعم | مُدار | نعم |
| التكلفة | VPS فقط | VPS فقط | حسب الاستخدام | مرتفع |
| التعقيد | منخفض | منخفض | منخفض جداً | مرتفع جداً |
| قيود المزود | لا يوجد | منخفض | مرتفع | منخفض |
| واجهة ويب | لا | نعم | نعم | لوحة تحكم |
| CI/CD مدمج | لا (عبر GH Actions) | نعم | نعم | لا |
يتميز Kamal بـ بساطته و عدم وجود قيود على المزود. ملف YAML واحد، بضعة أوامر، وتطبيقك في بيئة الإنتاج. لا لوحة تحكم ويب للإدارة، لا خدمة سحابية للدفع — فقط SSH و Docker وخادمك.
الخطوات التالية
الآن بعد نشر تطبيقك باستخدام Kamal 2، إليك كيفية المضي قدماً:
- أضف CDN مثل Cloudflare أمام خادمك لتحسين الأداء
- أعد تنبيهات باستخدام Uptime Kuma أو Better Stack للإشعار بالمشاكل
- أعد بيئة staging بملف تكوين ثانٍ (
config/deploy.staging.yml) - أتمت النسخ الاحتياطي لقاعدة بياناتك بمهمة cron على الخادم
- استكشف خطافات Kamal لتنفيذ سكربتات قبل/بعد النشر (ترحيل قاعدة البيانات، إبطال ذاكرة التخزين المؤقت، إلخ.)
الخلاصة
يمثل Kamal 2 نهجاً منعشاً في النشر: لا تعقيد غير ضروري، لا قيود على المزود، لا فواتير مفاجئة. مع خادم VPS بـ 5 يورو شهرياً وبضع دقائق من التكوين، تحصل على خط أنابيب نشر احترافي بدون توقف.
تتوافق فلسفة Kamal تماماً مع الحركة المتنامية نحو الاستضافة الذاتية والسيادة الرقمية. تحتفظ بالتحكم الكامل في بياناتك وبنيتك التحتية، مع الاستمتاع بتجربة مطور سلسة.
سواء كنت تنشر مشروعاً جانبياً أو شركة ناشئة أو تطبيق مؤسسة، يمنحك Kamal 2 القوة التي تحتاجها دون التعقيد غير الضروري. وإذا احتجت غداً لتغيير مزود الخادم، يكفي تعديل سطر واحد في ملف التكوين.
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

نشر تطبيق Next.js باستخدام Coolify v4: دليل شامل للاستضافة الذاتية
تعلّم كيفية نشر تطبيقات Next.js على خادمك الخاص باستخدام Coolify v4، البديل مفتوح المصدر لـ Vercel و Netlify. يشمل التثبيت والإعداد وشهادات SSL التلقائية وCI/CD والمراقبة.

نشر تطبيق Next.js باستخدام Docker و CI/CD في بيئة الإنتاج
تعلم كيف تُحاوِل تطبيق Next.js باستخدام Docker، وتُعدّ خط أنابيب CI/CD مع GitHub Actions، وتنشر تلقائياً في بيئة الإنتاج على خادم VPS. دليل شامل من التطوير إلى النشر الآلي.

Docker Compose للمطورين: Next.js مع PostgreSQL و Redis
تعلم كيفية تغليف تطبيق Next.js كامل مع PostgreSQL و Redis باستخدام Docker Compose. يغطي هذا الدليل العملي تنسيق الخدمات المتعددة وسير عمل التطوير وإعادة التحميل الفوري وفحوصات الصحة والإعدادات الجاهزة للإنتاج.