الأدلة التقنية

تثبيت n8n على سيرفرك للإنتاج بطريقة آمنة

كيف تثبت n8n مع PostgreSQL خلف Reverse Proxy، وتشغله بطابور وعدة Workers، وتحفظ مفتاح التشفير قبل أن تحتاجه.

تثبيت n8n على سيرفرك للإنتاج بطريقة آمنة

في مقال الأدوات شغلنا n8n في Container واحد بقاعدة SQLite، ورأينا أنه يحفظ كل تشغيل ويعيد ما فشل، ورأينا أيضاً أنه عملية واحدة تعمل على نواة واحدة. وهذا الإعداد مناسب للتجربة، ولكنه في الإنتاج يترك عدة مشاكل تنتظرك: قاعدة بيانات في ملف داخل Volume، ومفتاح تشفير لا تعرف أين هو، وعنوان Webhook خاطئ عندما تضعه خلف Reverse Proxy، وكود يكتبه أي مستخدم في عقدة Code ويعمل بجانب بيانات الاعتماد.

وفي هذا الدليل سوف نثبت n8n كما يجب أن يعمل في الإنتاج، ثم نكسره في الأماكن التي تنكسر فيها التثبيتات الحقيقية.

المواضيع في السلسلة:

وسوف نناقش في هذا المقال ما يلي:

  • معمارية n8n في وضع الطابور Queue Mode: الرئيسي Main وال Workers وRedis وPostgreSQL ومشغلات الكود Task Runners.
  • ملف Docker Compose كامل بإصدارات مثبتة، ومفتاح التشفير، وCaddy أمامه.
  • عنوان ال Webhook خلف ال Reverse Proxy، وماذا يحدث إذا نسيته.
  • قياس الحمل وتوزيعه على Worker اثنين، واستهلاك الذاكرة الحقيقي.
  • النسخ الاحتياطي والاستعادة، وما يحدث إذا فقدت مفتاح التشفير.
  • الترقيات، والمستخدمين والدخول الموحد SSO، والتحذيرات الأمنية في 2026 وكيف تتعامل معها.
📌
الترخيص الذي تثبت n8n تحته هو Sustainable Use License، وقد شرحنا ما يسمح به وما يحتاج ترخيصاً مدفوعاً في قسم الترخيص في مقال الأدوات، فاقرأه قبل أن تبني عليه خدمة لعملائك.

المعمارية: من يستقبل ومن ينفذ

في الوضع الافتراضي يقوم n8n بكل شيء في عملية واحدة: يعرض المحرر، ويستقبل ال Webhooks، وينفذ سير العمل Workflow. أما في وضع الطابور Queue Mode فيتوزع العمل كما في الصورة التالية:

المستخدمون والأنظمة الخارجية يصلون عبر المنفذ 443 إلى Caddy الذي يمرر إلى n8n main، والرئيسي يضع رقم التشغيل في Redis، وworker 1 وworker 2 يأخذان من Redis وينفذ كل منهما حتى 10 تشغيلات معاً ومعه runners لعزل عقدة Code، والنتيجة تكتب في PostgreSQL، وعلى اليمين قفل N8N_ENCRYPTION_KEY بنفس القيمة في main وفي كل worker
الرئيسي يستقبل وينشئ التشغيل، وRedis يحمل رقمه، وال Worker ينفذه ويكتب النتيجة في PostgreSQL

وفي الصورة أعلاه لاحظ التالي:

  • الرئيسي Main: يعرض المحرر وال API، ويستقبل ال Webhooks والمؤقتات، فينشئ التشغيل ولا ينفذه، ويضع رقمه في الطابور Queue.
  • Redis: يحمل أرقام التشغيلات Executions المنتظرة، فيأخذ أول Worker متاح الرقم التالي.
  • ال Worker: عملية n8n أخرى تقرأ سير العمل Workflow من قاعدة البيانات وتنفذه وتكتب النتيجة، وينفذ كل Worker حتى عشرة تشغيلات معاً افتراضياً، والتوثيق ينصح بألا تقل عن خمسة، لأن عدداً كبيراً من ال Workers بقيمة منخفضة يستنزف اتصالات قاعدة البيانات.
  • مشغلات الكود Task Runners: الكود الذي يكتبه المستخدم في عقدة Code يعمل في Container منفصل بجانب كل Worker، وسوف نشرح لماذا في قسم الأمان.
  • مفتاح التشفير: يجب أن يكون نفسه في الرئيسي وفي كل Worker، لأن ال Worker هو الذي يفك بيانات الاعتماد عندما ينفذ.

وقد يتساءل البعض: هل أحتاج وضع الطابور Queue Mode لفريق صغير؟ والإجابة أن ما تحتاجه دائماً هو PostgreSQL ومفتاح تشفير محفوظ ومشغلات كود خارجية، أما ال Workers فتحتاجها عندما يبدأ تشغيل ثقيل في إبطاء المحرر أو تأخير ال Webhooks الأخرى. ووضع الطابور Queue Mode نفسه موجود في النسخة المجانية Community، أما تعدد الرئيسي Multi-main لتوفر أعلى للرئيسي فمدفوع، كما في صفحة مقارنة النسخ.

قاعدة البيانات: PostgreSQL وليس SQLite

يستخدم n8n قاعدة SQLite افتراضياً، وهي ملف داخل مجلد البيانات، والتوثيق يذكر صراحة أن وضع الطابور Queue Mode مع SQLite غير مدعوم، فال Workers تحتاج قاعدة واحدة تصل إليها كلها. وبحسب صفحة اختيار قاعدة البيانات يدعم n8n آخر إصدارين من PostgreSQL مع إصدار ثالث للتوافق، أي 17 و18 ومعهما 16 كما في يوليو 2026، والصفحة نفسها تذكر أن النطاق يتغير كل نوفمبر مع إصدارات PostgreSQL. واخترنا 17.11، وقد شرحنا تثبيت PostgreSQL بصلاحيات واضحة في PostgreSQL للفرق، والاستعادة إلى أي لحظة في pgBackRest، فلا نعيدهما هنا.

التثبيت بملف Docker Compose

الملف التالي هو الذي شغلناه في المختبر بالإصدارات n8nio/n8n:2.43.4 وn8nio/runners:2.43.4 وpostgres:17.11 وredis:8.10.2 وcaddy:2.11.7-alpine، وكل الأسرار فيه تأتي من ملف .env بجانبه:

# Lab: n8n 2.43.4 in queue mode: main + 2 workers (each with a task runners sidecar), PostgreSQL 17, Redis, Caddy
name: lab-auto-n8n

x-n8n-env: &n8n-env
  DB_TYPE: postgresdb
  DB_POSTGRESDB_HOST: lab-auto-pg
  DB_POSTGRESDB_DATABASE: n8n
  DB_POSTGRESDB_USER: n8n
  DB_POSTGRESDB_PASSWORD: ${PG_PASSWORD}
  N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
  EXECUTIONS_MODE: queue
  QUEUE_BULL_REDIS_HOST: lab-auto-redis
  QUEUE_BULL_REDIS_PASSWORD: ${REDIS_PASSWORD}
  QUEUE_HEALTH_CHECK_ACTIVE: "true"
  OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: "true"
  N8N_RUNNERS_MODE: external
  N8N_RUNNERS_AUTH_TOKEN: ${RUNNERS_TOKEN}
  N8N_RUNNERS_BROKER_LISTEN_ADDRESS: 0.0.0.0
  N8N_HOST: n8n.localhost
  N8N_PROTOCOL: https
  N8N_WEBHOOK_URL: ${WEBHOOK_URL}
  N8N_PROXY_HOPS: ${PROXY_HOPS}
  GENERIC_TIMEZONE: Asia/Riyadh
  TZ: Asia/Riyadh
  N8N_DIAGNOSTICS_ENABLED: "false"
  N8N_PERSONALIZATION_ENABLED: "false"

services:
  lab-auto-pg:
    image: postgres:17.11
    container_name: lab-auto-pg
    environment:
      POSTGRES_DB: n8n
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${PG_PASSWORD}
    volumes: [lab-auto-pgdata:/var/lib/postgresql/data]
    healthcheck: { test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"], interval: 5s, retries: 20 }

  lab-auto-redis:
    image: redis:8.10.2
    container_name: lab-auto-redis
    command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}", "--appendonly", "yes"]
    volumes: [lab-auto-redisdata:/data]
    healthcheck: { test: ["CMD-SHELL", "redis-cli -a $$REDIS_PASSWORD --no-auth-warning ping"], interval: 5s, retries: 20 }
    environment: { REDIS_PASSWORD: "${REDIS_PASSWORD}" }

  lab-auto-n8n:
    image: n8nio/n8n:2.43.4
    container_name: lab-auto-n8n
    environment: *n8n-env
    volumes: [lab-auto-n8ndata:/home/node/.n8n]
    depends_on:
      lab-auto-pg: { condition: service_healthy }
      lab-auto-redis: { condition: service_healthy }

  lab-auto-worker1:
    image: n8nio/n8n:2.43.4
    container_name: lab-auto-worker1
    command: ["worker", "--concurrency=10"]
    environment: *n8n-env
    depends_on: [lab-auto-n8n]

  lab-auto-runners1:
    image: n8nio/runners:2.43.4
    container_name: lab-auto-runners1
    environment:
      N8N_RUNNERS_TASK_BROKER_URI: http://lab-auto-worker1:5679
      N8N_RUNNERS_AUTH_TOKEN: ${RUNNERS_TOKEN}
    depends_on: [lab-auto-worker1]

  lab-auto-worker2:
    image: n8nio/n8n:2.43.4
    container_name: lab-auto-worker2
    command: ["worker", "--concurrency=10"]
    environment: *n8n-env
    depends_on: [lab-auto-n8n]

  lab-auto-runners2:
    image: n8nio/runners:2.43.4
    container_name: lab-auto-runners2
    environment:
      N8N_RUNNERS_TASK_BROKER_URI: http://lab-auto-worker2:5679
      N8N_RUNNERS_AUTH_TOKEN: ${RUNNERS_TOKEN}
    depends_on: [lab-auto-worker2]

  lab-auto-caddy:
    image: caddy:2.11.7-alpine
    container_name: lab-auto-caddy
    ports: ["127.0.0.1:8443:443"]
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - lab-auto-caddydata:/data
    depends_on: [lab-auto-n8n]

volumes:
  lab-auto-pgdata:
  lab-auto-redisdata:
  lab-auto-n8ndata:
  lab-auto-caddydata:

networks:
  default:
    name: lab-auto-n8n-net

وملف .env تولده مرة واحدة كما يلي، ثم تحفظ نسخة منه في مخزن الأسرار لديك قبل أي شيء آخر، والسبب سوف تراه في قسم الاستعادة:

cat > .env <<EOF
PG_PASSWORD=$(openssl rand -hex 16)
REDIS_PASSWORD=$(openssl rand -hex 16)
RUNNERS_TOKEN=$(openssl rand -hex 24)
N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)
WEBHOOK_URL=https://n8n.example.com/
PROXY_HOPS=1
EOF
chmod 600 .env
docker compose up -d

وفي الإعداد أعلاه لاحظ التالي:

  • البيئة المشتركة x-n8n-env: الرئيسي وال Workers يأخذون المتغيرات نفسها من مكان واحد، فلا يمكن أن يختلف مفتاح التشفير أو عنوان ال Webhook بينها بالخطأ، وسوف نرى في الاختبار أدناه ماذا يحدث عندما يختلف.
  • N8N_ENCRYPTION_KEY: إذا لم تحدده يولد n8n مفتاحاً عشوائياً في أول تشغيل ويحفظه في ملف config داخل مجلد البيانات، كما في توثيق مفتاح التشفير، وهذا يعمل مع Container واحد، ولكنه في وضع الطابور Queue Mode يجب أن يكون متغيراً تعطيه للجميع.
  • EXECUTIONS_MODE: queue ومتغيرات QUEUE_BULL_REDIS_*: تفعل وضع الطابور Queue Mode وتربطه بـ Redis، ووضعنا لـ Redis كلمة مرور لأنه افتراضياً يعمل دونها.
  • OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: حتى التشغيل اليدوي من المحرر يذهب إلى ال Workers، فلا يحتاج الرئيسي مشغلات كود خاصة به.
  • N8N_RUNNERS_MODE: external: عقدة Code تعمل في Container runners منفصل لكل Worker، ويجب أن يكون إصداره هو إصدار n8n نفسه، كما في توثيق مشغلات الكود Task Runners.
  • لا يوجد منفذ منشور إلا منفذ Caddy: n8n وPostgreSQL وRedis لا تصل إليها إلا من الشبكة الداخلية، وفي المختبر ربطنا Caddy بالعنوان 127.0.0.1:8443، وفي سيرفر حقيقي يكون 443.

وملف Caddy الذي استخدمناه في المختبر يصدر شهادة من جهة داخلية لأن النطاق n8n.localhost محلي:

n8n.localhost {
	tls internal
	reverse_proxy lab-auto-n8n:5678
}

وعلى سيرفر حقيقي تحذف السطر tls internal وتضع نطاقك، فيصدر Caddy شهادة Let's Encrypt تلقائياً كما شرحنا في تثبيت Caddy.

وعند التشغيل كتب الرئيسي في سجله ما يلي (آخر الأسطر):

Version: 2.43.4
Building workflow dependency index...
Editor is now accessible via:
https://n8n.localhost:8443
Finished building workflow dependency index. Processed 0 draft workflows, 0 published workflows.

وكتب كل Worker عدد التشغيلات Executions التي يقبلها معاً، ثم سجل مشغلي الكود Task Runners من Container ال runners:

 * Concurrency: 10
 * Pool: (default)
 * Queue: jobs

Registered runner "launcher-javascript" (c42298a321128899)
Registered runner "launcher-python" (e48d1cbfe547220e) 

ثم أنشأنا حساب المالك Owner، وهو أول حساب ويملك كل الصلاحيات، فلا تترك المحرر مفتوحاً على الإنترنت قبل إنشائه، لأن أول من يصل إليه يصبح هو المالك.

عنوان ال Webhook خلف ال Reverse Proxy

يبني n8n عناوين ال Webhooks التي يعرضها في المحرر ويسجلها عند الخدمات الخارجية من N8N_PROTOCOL وN8N_HOST وN8N_PORT، أي من المنفذ الذي يعمل عليه داخلياً وهو 5678، بينما يصل إليه العالم على 443 عبر ال Reverse Proxy. لذلك يطلب التوثيق ثلاثة أشياء:

  • أن تحدد العنوان العام بالمتغير N8N_WEBHOOK_URL، وهو الاسم الجديد المتاح من الإصدار 2.30.0، وأصبح الاسم القديم WEBHOOK_URL مهملاً Deprecated من 2.35.0.
  • أن تضع N8N_PROXY_HOPS=1 عندما يوجد Reverse Proxy واحد أمامه.
  • أن يمرر آخر Proxy الترويسات X-Forwarded-For وX-Forwarded-Host وX-Forwarded-Proto، وCaddy يفعل ذلك افتراضياً.

ولكي نرى الفرق بنينا سير عمل Workflow صغيراً يرد بعنوان الاستئناف $execution.resumeUrl، وهو العنوان الذي يرسله n8n لمن يجب أن يوافق أو يرد في خطوة انتظار، مثل رابط موافقة يصل إلى مدير بالبريد. ومع الإعداد الصحيح كان الرد كما يلي:

curl -k --resolve n8n.localhost:8443:127.0.0.1 -X POST https://n8n.localhost:8443/webhook/urls
{"resumeUrl":"https://n8n.localhost:8443/webhook-waiting/2?signature=31293533169354a1d75c40c9b1e703ddc5f70f1689c36ffe0fc6201c4e382a4b","forwardedFor":"172.22.0.1"}

نكسره: نحذف العنوان العام

أعدنا تشغيل الرئيسي وحده دون N8N_WEBHOOK_URL ودون N8N_PROXY_HOPS، فأصبح يعلن عن عنوان المحرر بالمنفذ الداخلي:

WEBHOOK_URL= PROXY_HOPS=0 docker compose up -d lab-auto-n8n
Editor is now accessible via:
https://n8n.localhost:5678

ولكن الطلب نفسه بقي يرد بالعنوان الصحيح:

{"resumeUrl":"https://n8n.localhost:8443/webhook-waiting/523?signature=d0bc30e78a0793caf20cb15118c45f530865bdd430f81358e54214473493f432","forwardedFor":"172.22.0.1"}

والسبب أن التشغيل لا يحدث في الرئيسي، وإنما في ال Worker، وال Workers ما زالت تعمل بالإعداد القديم. وهذه نقطة يقع فيها كثيرون: تغير متغيراً في الرئيسي فتظن أنه طبق، ثم تجد أن نصف السلوك يأتي من ال Workers. وعندما أعدنا تشغيل ال Workers بالإعداد الخاطئ نفسه أصبح العنوان خاطئاً:

{"resumeUrl":"https://n8n.localhost:5678/webhook-waiting/524?signature=102cbc8fd80414714caecb15a536d8aa5983606f00a7a95bc7bdb197aefb0a4e","forwardedFor":"172.22.0.1"}

وهذا العنوان لا يصل إليه أحد من الخارج، فالمنفذ 5678 غير منشور أصلاً:

000 Failed to connect to n8n.localhost port 5678 after 2025 ms: Could not connect to server

أي أن رابط الموافقة الذي يصل إلى المدير لا يفتح، وسير العمل Workflow يبقى ينتظر إلى الأبد، ولا يظهر أي خطأ في n8n نفسه. والأمر نفسه يحدث مع العقد Nodes التي تسجل Webhook عند خدمة خارجية مثل GitHub Trigger، فتسجل عنواناً لا يصل إليه أحد. ثم أعدنا الإعداد الصحيح بالأمر docker compose up -d.

قياس الحمل على Worker اثنين

سير العمل Workflow في هذا الاختبار Webhook ثم عقدة Code بلغة JavaScript تحسب المجموع، أي أن كل طلب يمر على مشغلات الكود Task Runners الخارجية. وأرسلنا 500 طلب من عشرة عملاء متوازيين عبر Caddy، بعد عشرين طلباً للتسخين:

{"sent": 500, "concurrency": 10, "codes": {"200": 500}, "throughput_rps": 11.4, "resp_ms_p50": 868.4, "resp_ms_p95": 1305.8}

والذاكرة والمعالج من docker stats قبل الحمل:

lab-auto-caddy 0.00% 17.47MiB / 31.29GiB
lab-auto-n8n 0.00% 294.9MiB / 31.29GiB
lab-auto-pg 0.00% 71.71MiB / 31.29GiB
lab-auto-redis 0.26% 13.45MiB / 31.29GiB
lab-auto-runners1 0.00% 10.93MiB / 31.29GiB
lab-auto-runners2 0.04% 5.25MiB / 31.29GiB
lab-auto-worker1 0.09% 241.7MiB / 31.29GiB
lab-auto-worker2 0.19% 247.4MiB / 31.29GiB

وأثناء الحمل:

lab-auto-caddy 7.67% 22MiB / 31.29GiB
lab-auto-n8n 77.52% 368.1MiB / 31.29GiB
lab-auto-pg 57.32% 87.36MiB / 31.29GiB
lab-auto-redis 14.43% 17.01MiB / 31.29GiB
lab-auto-runners1 6.27% 155.9MiB / 31.29GiB
lab-auto-runners2 5.58% 153.5MiB / 31.29GiB
lab-auto-worker1 190.61% 303.6MiB / 31.29GiB
lab-auto-worker2 208.10% 314.6MiB / 31.29GiB

ثم عددنا في سجل كل Worker التشغيلات Executions التي بدأها، وسألنا قاعدة البيانات عن حالة التشغيلات Executions:

lab-auto-worker1 262 started, 262 finished
lab-auto-worker2 260 started, 260 finished

 status  |  mode   | count
---------+---------+-------
 success | webhook |   522

وفي النتائج أعلاه لاحظ التالي:

  • العدد 522 هو 500 طلب الاختبار مع عشرين طلب التسخين وطلبين قبلهما، وكلها نجحت، وتوزعت بالتساوي تقريباً على ال Worker اثنين دون أي إعداد منا.
  • الإعداد كله بثمانية Containers استهلك نحو 900 ميجابايت دون عمل، ونحو 1.4 جيجابايت تحت الحمل، وأغلبه في الرئيسي وال Workers.
  • كل Worker استخدم نحو نواتين أثناء الحمل، والرئيسي نحو ثلاثة أرباع نواة، وقاعدة البيانات أكثر من نصف نواة، لأن كل تشغيل يكتب في PostgreSQL.
  • في مقال الأدوات وصل n8n في Container واحد إلى 6.6 طلب في الثانية بسير عمل Workflow مختلف، وهنا 11.4، ولا نقارن الرقمين مباشرة لأن سير العمل Workflow مختلف والجهاز كان مشغولاً بأعمال أخرى، وإنما المهم أن العمل توزع وأن الرئيسي بقي متاحاً للمحرر.

وإذا أضفت Workers فتذكر أن كل واحد يفتح اتصالات بقاعدة البيانات، فراجع حد الاتصالات في PostgreSQL، وقد شرحنا ذلك في PostgreSQL للفرق.

النسخ الاحتياطي والاستعادة

بحسب توثيق النسخ الاحتياطي تتكون النسخة الكاملة من جزأين، والصورة التالية تبينهما وما يحدث عندما ينقص أحدهما:

ثلاثة صناديق في الأعلى: PostgreSQL بأمر pg_dump وفيه سير العمل والتشغيلات والمستخدمون وبيانات الاعتماد مشفرة، وفي الوسط ما يضاف إن وجد مثل العقد المخصصة والملفات الثنائية وإعداد ال Containers، وعلى اليسار N8N_ENCRYPTION_KEY يحفظ في مخزن أسرار منفصل. وفي الأسفل قاعدة البيانات مع المفتاح نفسه تعيد بيانات الاعتماد، وقاعدة البيانات مع مفتاح جديد تعطي خطأ Credentials could not be decrypted
قاعدة البيانات دون المفتاح تعيد سير العمل Workflow وتفقد كل بيانات الاعتماد

ولاحظ أن أوامر التصدير n8n export:workflow --backup وn8n export:credentials --backup ليست نسخة احتياطية كاملة، فالتوثيق نفسه يذكر أنها لا تحتوي المستخدمين وأدوارهم، ولا سجل التشغيلات Executions، ولا المتغيرات، ولا مفتاح التشفير، وأنها تكفي لنقل سير العمل Workflow بين نسختين وليس لاستعادة نسخة كاملة. والنسخة الصحيحة هي pg_dump لقاعدة البيانات، ومعها المفتاح، كما يلي:

docker exec lab-auto-pg pg_dump -U n8n -d n8n -Fc -f /tmp/n8n.dump
docker cp lab-auto-pg:/tmp/n8n.dump backup/n8n.dump

ثم استعدناها إلى قاعدة جديدة اسمها n8n_restore، ونظرنا إلى بيانات الاعتماد فيها:

docker exec lab-auto-pg psql -U n8n -d postgres -c "create database n8n_restore owner n8n;"
docker exec lab-auto-pg pg_restore -U n8n -d n8n_restore --no-owner /tmp/n8n.dump
docker exec lab-auto-pg psql -U n8n -d n8n_restore -c "select name, type, left(data, 24) || '...' as data_start from credentials_entity;"
       name        |      type      |         data_start
-------------------+----------------+-----------------------------
 Invoice API token | httpHeaderAuth | U2FsdGVkX19qOY5tRka1lQzi...
(1 row)

والحقل data مشفر بمفتاح التشفير، فمن يسرق نسخة قاعدة البيانات وحدها لا يقرأ بيانات الاعتماد، وهذا جيد، ولكنه يعني أيضاً أنك أنت لا تقرؤها إذا فقدت المفتاح.

نكسره: الاستعادة بمفتاح جديد

الآن سوف نفترض أن السيرفر احترق، وأن لديك نسخة قاعدة البيانات، ولكن ملف .env كان على السيرفر نفسه ولم يحفظ في مكان آخر، فشغلت n8n جديداً على القاعدة المستعادة بمفتاح جديد. وشغلنا بالمفتاح الجديد ثم بالمفتاح الأصلي أمر تصدير بيانات الاعتماد مفكوكة --decrypted، وهو الأمر الذي يحتاج المفتاح الصحيح:

docker run --rm --network lab-auto-n8n-net \
  -e DB_TYPE=postgresdb -e DB_POSTGRESDB_HOST=lab-auto-pg -e DB_POSTGRESDB_DATABASE=n8n_restore \
  -e DB_POSTGRESDB_USER=n8n -e DB_POSTGRESDB_PASSWORD="$PG_PASSWORD" \
  -e N8N_ENCRYPTION_KEY="$KEY" n8nio/n8n:2.43.4 export:credentials --all --decrypted

والمخرج سوف يكون كما يلي، بعد أن حذفنا منه أسطر التحذيرات التي لا علاقة لها بالاختبار:

== key: wrong
Acquiring database migration lock...
Could not read the instance identity from the DB, using derived values
Error exporting credentials. See log messages for details.
Credentials could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.
Credentials could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.
== key: original
Acquiring database migration lock...
[{"updatedAt":"2026-10-14T12:14:51.671Z","createdAt":"2026-10-14T12:14:51.671Z","id":"labAutoCred00001","name":"Invoice API token","data":{"name":"Authorization","value":"Bearer lab-secret-123"},"type":"httpHeaderAuth","isManaged":false,"isGlobal":false,"isResolvable":false,"resolvableAllowFallback":false,"resolverId":null,"usageScope":"project","pendingAuthorizationExpiresAt":null}]

وفي المخرج أعلاه لاحظ التالي:

  • بالمفتاح الجديد بدأ n8n وقرأ قاعدة البيانات بشكل طبيعي، فسير العمل Workflow موجود، ولكن كل بيانات الاعتماد لا تفك. وفي نسخة حقيقية فيها عشرات بيانات الاعتماد لعشرات الأنظمة، هذا يعني أن تعيد إنشاءها كلها وأن تطلب مفاتيح جديدة من كل نظام.
  • بالمفتاح الأصلي عادت القيمة كاملة كما أدخلناها.
  • لذلك احفظ المفتاح في مخزن الأسرار مع نسخ قاعدة البيانات، وليس في القرص نفسه، واختبر الاستعادة على سيرفر آخر بالمفتاح المحفوظ، كما شرحنا في النسخ الاحتياطي بقاعدة 3-2-1.
⚠️
الأمر export:credentials --decrypted يطبع كل بيانات الاعتماد نصاً واضحاً، والتوثيق يحذر من ذلك. استخدمناه هنا لإثبات أن المفتاح يعمل فقط، فلا تحفظ مخرجه في ملف، ولا تشغله على نسختك الحقيقية إلا عند الحاجة.

الأمان

n8n يحمل مفاتيح كل الأنظمة التي يربطها، فهو هدف ثمين، وفي 2026 نشرت n8n عدداً كبيراً من التحذيرات الأمنية في صفحة التحذيرات في المستودع: في القائمة التي اطلعنا عليها يوم 14 أكتوبر 2026 كان هناك 26 تحذيراً منشوراً في أغسطس 2026 (16 منها بخطورة عالية)، و49 في سبتمبر 2026 (27 منها عالية). وأشهر الثغرات التي يجب أن تعرفها:

الثغرةتاريخ النشرالخطورةما هيالإصلاح
CVE-2025-6861319 ديسمبر 20259.9 حرجةتعبيرات يكتبها مستخدم مسجل في سير العمل Workflow تنفذ كوداً على السيرفر بصلاحيات n8n1.120.4 و1.121.1 و1.122.0
CVE-2026-218588 يناير 202610.0 حرجةمهاجم دون حساب يقرأ ملفات من السيرفر عبر سير عمل Workflow يستخدم النماذج Forms، وسمته الشركة التي اكتشفتها Ni8mare1.121.0
GHSA-728h-pmr2-7cgh30 سبتمبر 2026عاليةتجاوز توقيع HMAC في خطوة الانتظار Send and Wait يسمح بالموافقة على تشغيل منتظر دون تسجيل دخول2.41.4 و2.42.1

والثغرة الأولى أضافتها وكالة CISA الأمريكية إلى قائمة الثغرات المستغلة فعلاً KEV في 11 مارس 2026، أي أن هناك من استغلها في هجمات حقيقية، وليست ثغرة نظرية. والدرس من هذه القائمة أن n8n ليس برنامجاً تثبته وتنساه، وإنما يحتاج تحديثاً منتظماً مثل نظام التشغيل نفسه.

وفيما يلي ما نطبقه على أي تثبيت:

  • مشغلات الكود Task Runners الخارجية: التوثيق يذكر أنها خط العزل الوحيد بين الكود الذي يكتبه المستخدم وn8n نفسه، وأن بدونها أو في الوضع الداخلي Internal يستطيع أي شخص يعدل سير عمل Workflow أن يقرأ قاعدة البيانات ومفتاح التشفير وبيانات الاعتماد. لذلك استخدم الوضع الخارجي External كما في ملف Compose أعلاه.
  • لا تنشر المحرر على الإنترنت إذا لم تحتج: انشر مسارات /webhook/ و/webhook-waiting/ فقط، واجعل المحرر خلف VPN أو خلف authentik Forward Auth.
  • احم كل Webhook: عقدة Webhook فيها حقل Authentication، أو تحقق من التوقيع في الخطوة التالية مباشرة.
  • شغل الفحص الأمني المدمج: الأمر n8n audit يفحص النسخة ويطبع تقريراً، وسوف نراه أدناه.
  • أغلق ما لا تستخدمه: مثل ال API العامة بالمتغير N8N_PUBLIC_API_DISABLED=true، والحزم المجتمعية Community Packages إذا لم تكن تحتاجها، لأن كل حزمة كود من طرف ثالث يعمل داخل n8n.
  • التحقق بخطوتين 2FA: يستطيع كل مستخدم تفعيله من إعداداته الشخصية في النسخة المجانية، فاطلبه من الجميع، أما فرضه على كل المستخدمين من سياسات الأمان فمتاح في Business وEnterprise.

وشغلنا الفحص الأمني على نسختنا:

docker exec lab-auto-n8n n8n audit

والمخرج طويل بصيغة JSON، وفيه ثلاثة أقسام للمخاطر هي credentials وnodes وinstance، ونعرض هنا جزءاً من قسم instance كما طبعه:

{
  "Instance Risk Report": {
    "risk": "instance",
    "sections": [
      {
        "title": "Unprotected webhooks in instance",
        "description": "This webhook node has the \"Authentication\" field set to \"None\" and is not directly connected to a node to validate the payload. Every unprotected webhook allows your workflow to be called by any third party who knows the webhook URL.",
        "recommendation": "Consider setting the \"Authentication\" field to an option other than \"None\", or validating the payload with one of the following nodes: n8n-nodes-base.if,n8n-nodes-base.switch,n8n-nodes-base.code,n8n-nodes-base.function,n8n-nodes-base.functionItem.",
        "location": [
          {
            "kind": "node",
            "workflowId": "labAutoCUrls0001",
            "workflowName": "Show my public URLs",
            "nodeId": "e1000000-0000-4000-8000-000000000003",
            "nodeName": "Webhook",
            "nodeType": "n8n-nodes-base.webhook"
          }
        ]
      },
      {
        "title": "Outdated instance",
        "description": "This n8n instance is outdated. Currently at version 2.43.4, missing 1 update.",
        "recommendation": "Consider updating this n8n instance to the latest version to prevent security vulnerabilities.",
        "nextVersions": [
          {
            "name": "2.43.5",
            "createdAt": "2026-10-14T10:42:25.990Z",
            "hasSecurityIssue": null,
            "hasSecurityFix": null,
            "securityIssueFixVersion": null,
            "hasBreakingChange": false,
            "documentationUrl": "https://docs.n8n.io/release-notes/#n8n2435?utm_source=n8n_app&utm_medium=instance_upgrade_releases",
            "nodes": [],
            "description": "Includes <strong>bug fixes</strong>"
          }
        ]
      },
      {
        "title": "Security settings",
        "description": "This n8n instance has the following security settings.",
        "recommendation": "Consider adjusting the security settings for your n8n instance based on your needs. See: https://docs.n8n.io/hosting/configuration/environment-variables/",
        "settings": {
          "features": {
            "communityPackagesEnabled": true,
            "versionNotificationsEnabled": true,
            "templatesEnabled": true,
            "publicApiEnabled": true
          },
          "nodes": {
            "nodesExclude": "n8n-nodes-base.executeCommand, n8n-nodes-base.localFileTrigger, n8n-nodes-base.e2eTest, n8n-nodes-base.dynamicCredentialCheck",
            "nodesInclude": "none"
          },
          "telemetry": {
            "diagnosticsEnabled": false
          }
        }
      }
    ]
  }
}

وفي المخرج أعلاه لاحظ التالي:

  • الفحص اكتشف أن ال Webhook في سير العمل Workflow Show my public URLs دون حماية، ولم يذكر سير عمل Workflow الطلبات، والسبب أنه متصل مباشرة بعقدة Code، والفحص يعتبرها مكاناً للتحقق من البيانات.
  • الفحص اكتشف أن إصداراً أحدث هو 2.43.5 صدر في اليوم نفسه الذي شغلنا فيه المختبر، أي أن النسخة تقادمت خلال ساعات، وهذا يؤكد ما قلناه عن التحديث المنتظم.
  • قسم الإعدادات يبين أن الحزم المجتمعية وال API العامة مفعلة افتراضياً، فأغلقها إن لم تكن تحتاجها.

الترقيات

تصدر n8n إصدارات بشكل متكرر جداً، وفي الأسبوع الذي كتبنا فيه هذا المقال صدر 2.42.4 و2.42.5 و2.42.6 و2.42.7 و2.43.0 إلى 2.43.5 و2.44.0 التجريبي، كما في صفحة الإصدارات. والفرع 1.x ما زال يستلم إصلاحات أمنية (1.123.84 في 8 أكتوبر 2026)، فإذا كنت عليه فلست مجبراً على القفز اليوم، ولكن خطط للانتقال. وطريقة الترقية التي نتبعها:

  1. اقرأ ملاحظات الإصدار، وانتبه إلى قسم Breaking Changes، فالانتقال من 1.x إلى 2.0 مثلاً غير تفعيل سير العمل Workflow من Active إلى Publish، وفي الإصدار 2.35.0 أصبح المتغير WEBHOOK_URL مهملاً لصالح N8N_WEBHOOK_URL.
  2. خذ نسخة pg_dump قبل الترقية، فالإصدار الجديد قد يعدل جداول قاعدة البيانات Migrations عند أول تشغيل، ولا يوجد رجوع تلقائي.
  3. غير الإصدار في الملف لكل من n8nio/n8n وn8nio/runners معاً، لأنهما يجب أن يتطابقا.
  4. جرب الترقية على نسخة اختبار من قاعدة البيانات أولاً إذا كانت لديك سير عمل Workflow حرجة.
  5. شغل docker compose pull ثم docker compose up -d، وراقب سجل الرئيسي حتى يظهر Editor is now accessible، ثم جرب سير عمل Workflow حرجاً.

ولم نشغل ترقية فعلية في هذا المختبر، والخطوات أعلاه من التوثيق ومن طريقة عملنا مع الأدوات الأخرى.

المستخدمون والدخول الموحد

في النسخة المجانية Community يوجد مالك واحد ومستخدمون يدعوهم، ويرى كل مستخدم سير العمل Workflow وبيانات الاعتماد التي أنشأها فقط، ولا توجد مشاركة بينهم، ولا مشاريع Projects. أما الدخول الموحد SSO عبر SAML أو OIDC أو LDAP فمتاح في خطتي Business وEnterprise للنسخة التي تستضيفها بنفسك. فإذا كانت مؤسستك تستخدم authentik كما شرحنا في تثبيت authentik، فإن ربط n8n به عبر OIDC يحتاج ترخيصاً مدفوعاً، والبديل في النسخة المجانية أن تضع المحرر خلف Forward Auth، مع بقاء حسابات n8n نفسها منفصلة.

أخطاء شائعة

  • ترك n8n يولد مفتاح التشفير ثم نسيانه: يعمل كل شيء حتى أول استعادة.
  • تغيير متغير في الرئيسي فقط: رأينا أن ال Workers هي التي تنفذ، فالإعداد المشترك يجب أن يصل إليها كلها.
  • نسيان N8N_WEBHOOK_URL خلف ال Reverse Proxy: روابط الموافقة وال Webhooks المسجلة عند الخدمات الخارجية تشير إلى منفذ لا يصل إليه أحد.
  • إصدار runners مختلف عن إصدار n8n: التوثيق يشترط تطابقهما.
  • اعتبار export --backup نسخة احتياطية: لا يحتوي المستخدمين ولا التشغيلات Executions ولا المفتاح.
  • نشر المحرر على الإنترنت دون حماية إضافية: مع عدد التحذيرات الأمنية في 2026، كل طبقة تقلل ما يصل إليه المهاجم.

تدريب عملي

  1. أضف Worker ثالثاً إلى ملف Compose مع Container runners له، وأعد قياس الحمل، ولاحظ هل يرتفع عدد الطلبات في الثانية أم يصبح PostgreSQL هو الحد.
  2. أوقف Redis أثناء الحمل لعشر ثوان ثم شغله، ولاحظ ماذا يحدث للطلبات التي وصلت أثناء التوقف.
  3. خذ نسخة pg_dump وانقلها مع المفتاح إلى جهاز آخر، واستعدها هناك، ثم تأكد أن سير عمل Workflow يستخدم بيانات اعتماد يعمل.

حذف المختبر

docker compose down -v

الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

  • في الإنتاج استخدم PostgreSQL ومفتاح تشفير تحدده أنت ومشغلات كود خارجية، ثم أضف وضع الطابور Queue Mode وال Workers عندما تحتاج.
  • الإعداد يجب أن يكون نفسه في الرئيسي وفي كل Worker، وأسهل طريقة لذلك بيئة مشتركة واحدة في ملف Compose.
  • خلف ال Reverse Proxy حدد N8N_WEBHOOK_URL وN8N_PROXY_HOPS، وإلا ظهرت روابط لا يصل إليها أحد دون أي خطأ.
  • النسخة الاحتياطية هي قاعدة البيانات مع مفتاح التشفير، وقاعدة البيانات وحدها تفقدك كل بيانات الاعتماد.
  • حدث n8n بانتظام، وشغل n8n audit، ولا تنشر المحرر على الإنترنت دون طبقة حماية.
  • SSO والمشاركة بين المستخدمين في الخطط المدفوعة.

وفي المقال القادم بإذن الله سوف نبني سير عمل Workflow حقيقياً على هذا التثبيت: من نموذج أو بريد إلى تذكرة في GLPI، ثم موافقة وتسليم جهاز في Snipe-IT، مع سير عمل Workflow أخطاء واختبار وحفظ في Git.

سجل التحديثات

  • أكتوبر 2026: كتابة المقال، وتشغيل المختبر على n8n 2.43.4 مع runners 2.43.4 وPostgreSQL 17.11 وRedis 8.10.2 وCaddy 2.11.7، والاطلاع على التوثيق والتحذيرات الأمنية بتاريخ 14 أكتوبر 2026.
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.