دليل عملي

نشر Payload CMS على VPS: دليل شامل

التطوير10 دقائق للقراءةعدد الخطوات: 6

يتثبت Payload CMS 3 مباشرة داخل تطبيق Next.js، مما يجعله من أكثر أنظمة إدارة المحتوى Headless قرباً من الكود. يغطي هذا الدليل النشر الكامل على VPS: إعداد Docker Compose مع healthchecks، وإدارة المستخدمين ومفاتيح API، وخطافات المجموعة، وسير عمل الترحيل، وحل الأخطاء الأكثر شيوعاً في الإنتاج.

المحتويات· لماذا استضافة Payload CMS ذاتياً على VPS1/10
  1. 01لماذا استضافة Payload CMS ذاتياً على VPS
  2. 02الفوائد الملموسة
  3. 03المتطلبات الأجهزية والبرمجية
  4. 04خطوات النشر
  5. 05المصادقة وإدارة الوصول
  6. 06خطافات المجموعة: الاستجابة لأحداث المحتوى
  7. 07التحديثات وترحيل المخطط
  8. 08عندما يرفض `sharp` التحميل
  9. 09الحل: الأخطاء الشائعة في الإنتاج
  10. 10Payload CMS مقابل Strapi: أي CMS headless تستضيفه ذاتياً؟

لماذا استضافة Payload CMS ذاتياً على VPS

يعتمد Payload CMS نهجاً قائماً على الكود بشكل جذري: يُعرَّف مخطط المحتوى في TypeScript ضمن ملفات إعداد مُصنَّفة بـ Git، ومنذ الإصدار 3 يتثبت Payload مباشرة داخل تطبيق Next.js عبر App Router. هذا يعني أن VPS يتيح لك استضافة نظام إدارة المحتوى والواجهة الأمامية في عملية Node واحدة، تشترك في نفس البناء والـ runtime. تحصل على typing شامل من طرف إلى طرف، وترحيل مخطط مُدار في الكود، دون أي اعتماد على واجهة رسومية لنمذجة المحتوى. الاستضافة الذاتية هي الخيار الطبيعي هنا: صُمِّم Payload للنشر مثل أي تطبيق Next.js، ويمنحك VPS السيطرة الكاملة على قاعدة البيانات (MongoDB أو PostgreSQL)، والرفوعات، ومتغيرات البيئة، دون وسيط.

الفوائد الملموسة

  • مخطط المحتوى مُعرَّف بـ TypeScript ومُصنَّف بـ Git: مراجعة كاملة للكود والتاريخ
  • نظام إدارة المحتوى والواجهة الأمامية في runtime واحد: بناء واحد وعملية واحدة للنشر
  • Typing شامل بين إعداد Payload وAPI والواجهة، دون توليد يدوي
  • اختيار قاعدة البيانات: MongoDB أو PostgreSQL عبر المحولات الرسمية
  • ترحيل المخطط مُقاد بالكود (payload migrate)، قابل للتكرار بين البيئات
  • Local API: وصول مباشر للبيانات دون استدعاءات HTTP من كود Next.js الخادم
  • لا تكاليف ترخيص: Payload CMS 3 مفتوح المصدر (MIT) — تدفع فقط مقابل VPS

المتطلبات الأجهزية والبرمجية

بما أن Payload يعمل على Next.js، فإن البناء مُطالِب بالموارد: خطط لـ VPS بـ 2 vCPU و4 GB RAM لبناء التطبيق وتشغيله بشكل مريح. ثبِّت Node.js 20 LTS وDocker وDocker Compose. لقاعدة البيانات، جهِّز MongoDB 7 أو PostgreSQL 16 وفق المحوِّل المختار (@payloadcms/db-mongodb أو @payloadcms/db-postgres). وجِّه نطاقك نحو IP الـ VPS. خصص 15 GB قرصياً لـ node_modules وبناء .next والرفوعات ونسخ قاعدة البيانات الاحتياطية.

خطوات النشر

  1. اختيار محوِّل قاعدة البيانات وإعداده

    في payload.config.ts، صرِّح بالمحوِّل: mongooseAdapter لـ MongoDB أو postgresAdapter لـ PostgreSQL، مع قراءة URL الاتصال من DATABASE_URI. هذا خيار هيكلي: PostgreSQL يستلزم ترحيلات، بينما MongoDB أكثر مرونة في المخطط.

  2. تحضير متغيرات البيئة

    أنشئ ملف .env.production يحتوي على الحد الأدنى:

    PAYLOAD_SECRET=سرك-32-حرفاً-على-الأقل
    DATABASE_URI=postgresql://user:pass@postgres:5432/payload
    NEXT_PUBLIC_SERVER_URL=https://example.com
    NODE_ENV=production
    PAYLOAD_CONFIG_PATH=src/payload.config.ts

    PAYLOAD_SECRET يُشفِّر رموز JWT وكوكيز الجلسة — قيمة قصيرة أو متوقعة تُضعِف نظام المصادقة بأكمله. ولِّدها بـ openssl rand -hex 32. يجب أن يطابق NEXT_PUBLIC_SERVER_URL URL العام النهائي: يستخدمه Payload لبناء روابط الوسائط وعناوين البريد الإلكتروني.

  3. كتابة Docker Compose مع healthchecks

    يتضمن docker-compose.yml المتين healthchecks لضمان عدم بدء التطبيق إلا بعد جاهزية قاعدة البيانات:

    services:
      app:
        build: .
        ports:
          - "127.0.0.1:3000:3000"
        env_file: .env.production
        depends_on:
          postgres:
            condition: service_healthy
        volumes:
          - uploads:/app/public/media
    
      postgres:
        image: postgres:16-alpine
        environment:
          POSTGRES_USER: payload
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
          POSTGRES_DB: payload
        volumes:
          - pgdata:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U payload"]
          interval: 5s
          timeout: 5s
          retries: 10
    
    volumes:
      pgdata:
      uploads:

    بدون healthcheck وشرط service_healthy، يبدأ التطبيق قبل قبول PostgreSQL للاتصالات، ويفشل تجمع الاتصالات ويعيد تشغيل الحاوية في حلقة.

  4. بناء الصورة وتنفيذ الترحيلات

    ابنِ ثم نفِّذ الترحيلات بهذا الترتيب:

    docker compose build
    docker compose up -d postgres
    docker compose run --rm app npx payload migrate
    docker compose up -d app

    لـ PostgreSQL، يطبق npx payload migrate ملفات الترحيل المولَّدة في src/migrations/. إن لم تكن موجودة بعد، ولِّدها أولاً بـ npx payload migrate:create. لـ MongoDB، يُطبَّق المخطط تلقائياً عند أول تشغيل.

  5. إعداد Nginx كـ reverse proxy

    وجِّه vhost نحو http://127.0.0.1:3000 (المنفذ الافتراضي لـ Next.js). زِد client_max_body_size لرفع الوسائط وأضف ترويسات X-Forwarded-Proto لكي يولِّد Payload URLs صحيحة بـ HTTPS.

  6. التأمين بـ SSL وتثبيت URL الخادم

    نفِّذ certbot --nginx -d example.com، ثم تحقق من تعريف serverURL: 'https://example.com' في payload.config.ts. هذا URL يتحكم في روابط الوسائط ورسائل إعادة تعيين كلمة المرور والعمل الصحيح للوحة إدارة خلف الـ proxy.

المصادقة وإدارة الوصول

يدير Payload 3 المستخدمين عبر مجموعة users المعرَّفة في payload.config.ts. فعِّل المصادقة على المجموعة بـ auth: true — هذا يضيف تلقائياً نقاط النهاية /api/users/login و/api/users/logout و/api/users/me و/api/users/refresh-token.

للأدوار، لا يفرض Payload نموذجاً: عرِّف حقل role (نوع select) على مجموعة users، ثم تحكم في الوصول عبر دوال access على مستوى كل مجموعة وكل عملية:

access: {
  read: ({ req: { user } }) => user?.role === 'admin',
  create: isAdmin,
  update: isAdminOrSelf,
  delete: isAdmin,
}

للوصول البرمجي (CI والتكاملات الخارجية)، استخدم مفاتيح API: فعِّل useAPIKey: true في إعداد مصادقة المجموعة. يمكن لكل مستخدم حينئذٍ توليد مفتاح من لوحة الإدارة. مرِّره في ترويسة Authorization: users API-Key مفتاحك. مفاتيح API مُجزَّأة في قاعدة البيانات — المفتاح المفقود لا يُسترد، يُعاد توليده فقط.

خطافات المجموعة: الاستجابة لأحداث المحتوى

تتيح خطافات المجموعة في Payload 3 تنفيذ كود قبل أو بعد كل عملية CRUD. تحلّ محلّ الـ webhooks بشكل أنيق حين تعيش المنطق في نفس الـ runtime:

hooks: {
  afterChange: [
    async ({ doc, operation }) => {
      if (operation === 'create') {
        await notifySubscribers(doc)
      }
    },
  ],
  beforeDelete: [
    async ({ id }) => {
      await cleanupMedia(id)
    },
  ],
}

الخطافات المتاحة: beforeOperation وbeforeValidate وbeforeChange وafterChange وbeforeRead وafterRead وbeforeDelete وafterDelete. خطاف afterChange مثالي لإبطال كاش CDN أو إرسال إشعار أو مزامنة مع خدمة خارجية بعد النشر.

التحديثات وترحيل المخطط

يتبع Payload 3 الإصدار الدلالي. تحديثات الـ patch (3.x.y → 3.x.z) آمنة التطبيق دون ترحيلات. قد تضيف تحديثات الـ minor (3.x → 3.y) أعمدة أو فهارس وتستلزم تشغيل payload migrate.

سير العمل الموصى به لكل تحديث:

# 1. تحديث الإصدار في package.json
npm install [email protected] @payloadcms/[email protected]

# 2. توليد ملف ترحيل إذا تغيّر المخطط
npx payload migrate:create

# 3. الاختبار محلياً على نسخة قاعدة البيانات
npx payload migrate

# 4. حفظ ملف الترحيل مع تحديث الإصدار
git add src/migrations/ package.json package-lock.json
git commit -m "chore: payload 3.88.0"

# 5. في الإنتاج: إيقاف التطبيق، تطبيق الترحيلات، إعادة التشغيل
docker compose run --rm app npx payload migrate
docker compose up -d app

لا تعدِّل يدوياً الملفات المولَّدة في src/migrations/: يتحقق Payload منها بالـ hash. ملف معدَّل يُفشِل migrate برسالة Error: Migration file has been modified since it was created.

عندما يرفض `sharp` التحميل

هذا أكثر عائق شائع عند النشر على VPS، ويأخذ ثلاثة أشكال متميزة. الأكثر إرباكاً هو Unsupported CPU: prebuilt binaries for linux-x64 require v2 microarchitecture: يصدر من معالج الجهاز، لا من كودك أو تبعياتك. الثنائيات المُجمَّعة مسبقاً تستهدف مجموعة تعليمات لا تتوفر في المعالجات الأقدم — هذا معيار اختيار VPS، لا خطأ يُصلَح. الثاني Could not load the "sharp" module using the linux-x64 runtime، يشير عادةً إلى node_modules مبني على منصة مختلفة — أعِد التثبيت على الجهاز الهدف. الثالث خاص بـ Docker متعدد المراحل: sharp مُثبَّت في مرحلة البناء فقط غائب عن مرحلة التشغيل — ثبِّته صراحةً في المرحلة النهائية.

الحل: الأخطاء الشائعة في الإنتاج

نفاد ذاكرة البناء — Killed أو JavaScript heap out of memory.
يحدث أثناء npm run build حين تقل الذاكرة المتاحة عن 3 GB. Node.js محدود بـ ~1.8 GB افتراضياً. مرِّر NODE_OPTIONS=--max-old-space-size=3072 قبل البناء، أو ابنِ الصورة على جهاز أقوى وادفعها إلى registry.

PostgreSQL — Error: connect ECONNREFUSED 127.0.0.1:5432.
التطبيق يحاول الاتصال قبل جاهزية PostgreSQL، أو URL الاتصال يشير إلى localhost بدلاً من اسم خدمة Docker (postgres). تحقق من استخدام DATABASE_URI لاسم الخدمة ووجود healthcheck (انظر الخطوة 3).

لوحة الإدارة غير متاحة في الإنتاج — /admin يُعيد 404 أو حلقة إعادة توجيه.
سببان رئيسيان: serverURL غير مُعرَّف أو غير مطابق للـ URL الفعلي، أو ترويسة X-Forwarded-Proto: https غائبة من Nginx. أضف proxy_set_header X-Forwarded-Proto $scheme; للـ vhost وتحقق من تطابق serverURL مع الأصل العام دون شرطة مائلة نهائية.

CORS — Access-Control-Allow-Origin غائبة عن الـ API.
Payload يقرأ cors من payload.config.ts. في الإنتاج، اذكر الأصول المسموحة صراحةً: cors: { origins: ['https://example.com'] }. cors: { origins: [serverURL] } هو الإعداد الآمن الأدنى.

Error: Migration file has been modified since it was created.
ملف في src/migrations/ عُدِّل يدوياً. استعِد الأصلي من Git، ولِّد ملف ترحيل جديداً إذا تغيّر المخطط، وأعِد التطبيق بالترتيب.

Payload CMS مقابل Strapi: أي CMS headless تستضيفه ذاتياً؟

مرّر الجدول أفقيًا

المعيارPayload CMSStrapi
نهج الإعدادCode-first بـ TypeScript مُصنَّف بـ GitGUI-first عبر Content-Type Builder
تكامل الواجهة الأماميةأصيل في Next.js (runtime واحد)منفصل، واجهة أمامية ونظام CMS منفصلان
قواعد البيانات المدعومةMongoDB وPostgreSQLPostgreSQL وMySQL وSQLite
TypingTypeScript أصيل من طرف إلى طرفأنواع مولَّدة، تكامل أقل إحكاماً
الوصول للبيانات من الخادمLocal API دون استدعاءات HTTPAPI REST/GraphQL عبر HTTP
نمذجة المحتوىفي الكود، من قِبل المطورينفي الواجهة، متاح لغير المطورين
RAM اللازمة للبناءعالية (بناء Next.js)عالية (بناء React admin)
إدارة الترحيلاتملفات مُصنَّفة، `payload migrate`ترحيلات تلقائية عبر Strapi CLI
مفاتيح API الأصيلةنعم، لكل مجموعة مع التجزئةنعم، عبر رموز API
خطافات المجموعةأصيلة ومكتوبة بـ TypeScriptlifecycle hooks عبر middleware
مثالي لـفرق المطورين، مشاريع Next.js المكتوبةالفرق المختلطة، النمذجة المرئية

استغل Local API الخاصة بـ Payload في مكونات الخادم Next.js: بدلاً من استدعاء API الخاصة بك عبر fetch، استورد getPayload واستعلم قاعدة البيانات مباشرةً (payload.find({ collection: 'posts' })). تلغي رحلة HTTP وتكسب في زمن الاستجابة والأمان. للوسائط، ثبِّت إضافة @payloadcms/storage-s3 لتخزين الرفوعات خارج الـ VPS، وأتمت نسخاً احتياطية يومية لقاعدة البيانات عبر cron. أخيراً، فعِّل سير عمل المسودة/المعاينة الأصيل في Payload لتقديم خطوة معاينة للمحررين قبل النشر.

انشر Payload CMS على حزمة موحّدة

يوفّر خادم VPS Cloud من ServOrbit القدرة اللازمة لبناء Next.js الخاص بـ Payload وبيئة Docker جاهزة لـ MongoDB أو PostgreSQL، لاستضافة نظام إدارة المحتوى الذي يعتمد الشيفرة أوّلًا وواجهتك الأمامية على الآلة نفسها.

بحاجة إلى مساعدة؟

تصفّح مركز المساعدة والأسئلة الشائعة، أو تواصل مع فريقنا — معاودة اتصال أو WhatsApp أو بريد إلكتروني. الدعم بـالعربية والفرنسية والإنجليزية.

راسلنا على WhatsAppيُفتح في علامة تبويب جديدة