دليل النشر

تثبيت Windmill على خادم VPS: أتمتة ذاتية الاستضافة

انشر على VPS Cloud ←

دليل عملي

الأتمتة

تثبيت Windmill على خادم VPS: أتمتة ذاتية الاستضافة

Windmill منصة مفتوحة المصدر تحوّل الشيفرة (Python وTypeScript وGo وBash وSQL) إلى سير عمل وواجهات API وواجهات داخلية. مُصمّمة للمطوّرين، وتجمع بين مرونة الشيفرة وسرعة الـ no-code. باستضافتها الذاتية على خادم VPS، تصبح محرّك الأتمتة لديك وأداة بناء التطبيقات الداخلية، دون الاعتماد على خدمة SaaS.

13 دقيقةً للقراءةعدد الخطوات: 7
المحتويات· لماذا تستضيف Windmill ذاتيًا على خادم VPS1/12
  1. 01لماذا تستضيف Windmill ذاتيًا على خادم VPS
  2. 02فوائد ملموسة للاستضافة الذاتية
  3. 03المتطلبات التقنية
  4. 04حجّم الخادم بحسب الاستخدام
  5. 05انشر Windmill باستخدام Docker Compose
  6. 06التهيئة بعد التثبيت
  7. 07الإعدادات الأساسية
  8. 08مثال: سكربت Python من البداية إلى النهاية
  9. 09تقوية النسخة
  10. 10استكشاف الأعطال: أخطاء النشر الأول
  11. 11الأعراض وطرق الإصلاح
  12. 12Windmill أم n8n أم Temporal

لماذا تستضيف Windmill ذاتيًا على خادم VPS

يعمل Windmill باستمرار على خادمك ويعرض كل سكربت من سكربتاتك كنقطة REST مصادَق عليها برمز (token)، أو كمهمة مجدولة، أو كواجهة مولَّدة تلقائيًا. وعلى خلاف الدوال عديمة الخادم (serverless)، لا يوجد إقلاع بارد ولا حد تنفيذ يفرضه طرف ثالث. تصل سكربتات Python أو TypeScript أو Bash مباشرةً إلى قواعد البيانات والخدمات الداخلية على خادمك، دون المرور ببوابة سحابية. الواجهة الخلفية مكتوبة بلغة Rust: خوادم API عديمة الحالة وعمّال (workers) يسحبون المهام من طابور مخزَّن في PostgreSQL، ما يسمح بإضافة عمّال دون المساس ببقية المكوّنات.

يتوقف الاختيار أساسًا على طريقة عملك. إن كانت حاجتك ربط خدمات SaaS ببعضها على لوحة مرئية، يبقى n8n أكثر مباشرة؛ وللعمليات التجارية الطويلة جدًا التي تتطلب ضمان الاستئناف، صُمّم Temporal لهذا الغرض. يستهدف Windmill الفرق التي تكتب الشيفرة أصلًا وتريد جعلها قابلة للتنفيذ والجدولة والاستخدام من غير المطوّرين في دقائق. أما الترخيص، فالنواة بترخيص AGPLv3 وصورة Community Edition مجانية، مع حصص وميزات محجوزة لإصدار Enterprise؛ وإعادة بيعها أو تقديمها كخدمة مُدارة لعملائك تتطلب ترخيصًا تجاريًا.

فوائد ملموسة للاستضافة الذاتية

  • لا فوترة عند كل تنفيذ: آلاف المهام يوميًا دون فاتورة حوسبة سحابية.
  • وصول مباشر إلى قواعد البيانات والخدمات الداخلية على الخادم، دون نفق.
  • أسرار ومفاتيح API مخزّنة مشفّرة داخل Windmill ولا تغادر خادمك أبدًا.
  • نماذج إدخال ونقاط REST تُولَّد تلقائيًا لكل سكربت انطلاقًا من توقيع دالته main.
  • عمّال إضافيون يمكن إضافتهم أثناء التشغيل لاستيعاب ذروات الحمل.
  • عدة مساحات عمل (workspaces) معزولة على نسخة واحدة: واحدة لكل فريق أو مشروع أو بيئة.
  • إدارة الإصدارات: يُحتفظ بكل نشر للسكربت، ويدفع Git Sync مساحة العمل إلى مستودع Git (مضمَّن في Community Edition لمساحات العمل التي لا يتجاوز مستخدموها اثنين).

المتطلبات التقنية

يتطلب Windmill معالجَين vCPU على الأقل و4 غيغابايت من RAM. تشغّل الحزمة الرسمية PostgreSQL 18، والخادم، وثلاثة عمّال عامّين بحد ذاكرة 2 غيغابايت لكل منهم، وعاملًا أصيلًا (native) للمهام الخفيفة، والخدمة windmill_extra (الإكمال التلقائي عبر LSP ومصحّح أخطاء المحرر)، وCaddy في الواجهة: لذا قلّل عدد العمّال على خادم بسعة 4 غيغابايت. احسب 20 غيغابايت من القرص لقاعدة البيانات والسجلّات وذاكرة التخزين المؤقت لاعتماديات العمّال، التي تكبر مع كل حزمة pip أو npm مستخدمة. يكفي Ubuntu 22.04/24.04 أو Debian 12 مع Docker Engine وإضافة Docker Compose v2، إضافةً إلى اسم نطاق يشير إلى الخادم من أجل شهادة TLS. لا حاجة إلى GPU. إذا كنت تخطط لتشغيل عمّال Python ثقيلة (تعلّم آلي، أو pandas على مجموعات بيانات كبيرة)، فاختر 4 vCPU و8 غيغابايت من RAM على الأقل.

حجّم الخادم بحسب الاستخدام

  • قاعدة الناشر: عامل واحد تقريبًا لكل vCPU، ومن 1 إلى 2 غيغابايت من RAM لكل عامل. المهمة التي تتجاوز حد ذاكرة عاملها (2048 ميغابايت افتراضيًا) تُقتل برمز الخروج 137.
  • استخدام فردي أو تجربة: 2 vCPU و4 غيغابايت من RAM و20 غيغابايت من القرص، مع عامل عام واحد إضافةً إلى العامل الأصيل. يكفي لسكربتات مجدولة وبضع نقاط webhook.
  • فريق صغير: 4 vCPU و8 غيغابايت من RAM و40 غيغابايت من القرص. يتسع المضيف نفسه للعمّال الثلاثة في الملف الرسمي ولـ PostgreSQL دون إشباع الذاكرة.
  • عدة فرق أو مهام Python ثقيلة: 8 vCPU و16 غيغابايت من RAM و80 غيغابايت من القرص لستة إلى ثمانية عمّال. في هذه المرحلة، تبسّط قاعدة PostgreSQL منفصلة النسخ الاحتياطي وترقية الإصدارات.

انشر Windmill باستخدام Docker Compose

  1. نزّل الملفات الرسمية

    اتصل بخادمك عبر SSH ونزّل الملفات المرجعية الثلاثة المنشورة في مستودع Windmill: ملف docker-compose.yml، وملف Caddyfile الخاص بالوكيل العكسي، وملف .env:

    mkdir -p /opt/windmill && cd /opt/windmill
    curl -fsSL https://raw.githubusercontent.com/windmill-labs/windmill/main/docker-compose.yml -o docker-compose.yml
    curl -fsSL https://raw.githubusercontent.com/windmill-labs/windmill/main/Caddyfile -o Caddyfile
    curl -fsSL https://raw.githubusercontent.com/windmill-labs/windmill/main/.env -o .env

    لا حاجة إلى استنساخ المستودع كاملًا. تتبع هذه الملفات الفرع main: أعد قراءتها قبل كل تحديث، لأن الحزمة تتطور (الانتقال إلى PostgreSQL 18، والخدمة windmill_extra…).

  2. املأ ملف `.env`

    لا يحمل ملف .env إلا متغيرين. استبدل كلمة مرور PostgreSQL الافتراضية، وثبّت الصورة على إصدار منشور بدلًا من main (الإصدار 1.864.1 بتاريخ 06/11/2026، تحقّق منه في صفحة الإصدارات على GitHub):

    DATABASE_URL=postgres://postgres:MOT_DE_PASSE_FORT@db/windmill?sslmode=disable
    WM_IMAGE=ghcr.io/windmill-labs/windmill:1.864.1

    انسخ كلمة المرور نفسها إلى POSTGRES_PASSWORD تحت الخدمة db في ملف docker-compose.yml. المضيف db هو اسم خدمة PostgreSQL داخل الحزمة: لا تستبدله بـ localhost. لاستخدام قاعدة بيانات موجودة، وجّه DATABASE_URL إليها واضبط الخدمة db على replicas: 0.

  3. شغّل الحزمة

    شغّل المجموعة كاملة وتابع إقلاع الخادم:

    docker compose up -d
    docker compose ps
    docker compose logs -f windmill_server

    يجب أن يعرض docker compose ps الخدمات db وwindmill_server وثلاث نسخ من windmill_worker وwindmill_worker_native وwindmill_extra وcaddy؛ ويبقى مفهرس البحث النصي الكامل (windmill_indexer) بلا نسخ، لأنه ميزة من إصدار Enterprise. ينتظر الخادم أن تجتاز PostgreSQL فحص الصحة، ثم يطبّق بنفسه ترحيلات المخطط. يستمع على المنفذ 8000 داخل شبكة Docker دون أن يُنشر على المضيف: Caddy هو من يستقبل حركة المرور.

  4. هيّئ الوكيل العكسي وTLS

    الأبسط أن تترك Caddy المرفق يحصل على شهادة Let's Encrypt. في الخدمة caddy داخل ملف docker-compose.yml، ألغِ تعليق المنفذ 443 واستبدل BASE_URL=":80" باسم نطاقك، ثم أعد تشغيل docker compose up -d:

      caddy:
        ports:
          - 80:80
          - 25:25
          - 443:443
        environment:
          - BASE_URL=windmill.example.com

    أبقِ المنفذ 80 مفتوحًا لعملية التحقق. إن كان nginx يخدم مواقع أخرى على الخادم أصلًا، فاحذف الخدمة caddy، وانشر 127.0.0.1:8000:8000 على windmill_server و127.0.0.1:3000:3000 على windmill_extra، ثم أعد إنتاج توجيه Caddyfile: تذهب المسارات /ws/ و/ws_mp/ و/ws_debug/ إلى windmill_extra، وكل ما عداها إلى الخادم.

    location ~ ^/ws(_mp|_debug)?/ {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    الترويستان Upgrade وConnection ضروريتان: يستخدم المحرر WebSockets للإكمال التلقائي وتصحيح الأخطاء. تحصل بعد ذلك على الشهادة عبر certbot --nginx -d windmill.example.com.

  5. سجّل الدخول وأمّن النسخة

    افتح https://windmill.example.com وسجّل الدخول بحساب المشرف الأعلى الافتراضي [email protected] / changeme. يقترح معالج التشغيل الأول استبداله بحسابك الخاص: افعل ذلك، لأنك إن تخطّيت هذه الخطوة تبقى بيانات الدخول الافتراضية فعّالة، وهي الثغرة الأكثر شيوعًا في نسخ Windmill المعروضة علنًا. بعد ذلك، اضبط في Instance settings قيمة Base URL على العنوان العام بصيغة https://: يستخدمها Windmill لبناء عناوين webhook وروابط العودة في الـ flows. أنشئ أخيرًا أول مساحة عمل بمعرّف قصير (prod أو data-team).

  6. اضبط عدد العمّال

    يشغّل الملف الرسمي ثلاث نسخ من windmill_worker، لكل منها حد 2048 ميغابايت. على خادم بسعة 4 غيغابايت، اخفض العدد إلى واحد أو اثنين؛ وعلى جهاز أكبر، ارفعه إلى عامل واحد لكل vCPU:

    docker compose up -d --scale windmill_worker=2
    docker compose ps windmill_worker

    لجعل الإعداد دائمًا، عدّل deploy.replicas الخاص بالخدمة في ملف docker-compose.yml. لا يحتاج الخادم إلى إعادة تشغيل: يسجّل العمّال أنفسهم ويظهرون في صفحة Workers من الواجهة مع مجموعتهم (default أو native).

  7. حدّث Windmill

    غيّر الوسم في WM_IMAGE بعد قراءة ملاحظات الإصدار، ثم:

    docker compose pull
    docker compose up -d
    docker image prune

    يُنزّل pull الصور الجديدة بينما لا تزال الحاويات القديمة تعمل؛ وعند إعادة التشغيل يرحّل الخادم قاعدة البيانات بنفسه، ويُنهي العمّال مهامهم الجارية ضمن مهلة الإيقاف في Docker (30 ثانية). خذ نسخة احتياطية من القاعدة قبل ذلك مباشرةً، ولا تشغّل docker image prune -a إلا إذا كان الخادم لا يستضيف شيئًا آخر: فهذا الأمر يحذف أيضًا صور المشاريع الأخرى.

التهيئة بعد التثبيت

بعد أن تصبح النسخة متاحة، تحدد ستة إعدادات طريقة عمل سكربتاتك في الإنتاج. تُضبط جميعها من الواجهة، مساحة عمل تلو الأخرى.

الإعدادات الأساسية

  • مساحات العمل: تعزل مساحة العمل السكربتات والـ flows والأسرار والأعضاء. أنشئ واحدة لكل فريق أو بيئة بدلًا من خلط كل شيء، واحجز دور المشرف الأعلى (superadmin) لإدارة النسخة.
  • المتغيرات والأسرار: تُشفَّر كل المتغيرات بمفتاح متماثل خاص بمساحة العمل؛ والمتغيرات الموسومة كأسرار لا تعود تظهر في الواجهة بعد إدخالها. هنا مكان رموز API وكلمات المرور، لا في شيفرة السكربت أبدًا.
  • الموارد: كائنات JSON ذات أنواع محددة (اتصال PostgreSQL، حساب SMTP، بيانات اعتماد OAuth). المعامل من نوع postgresql في سكربت ما لا يقترح عندها في النموذج المولَّد إلا الموارد من هذا النوع.
  • الجدولة: يقبل كل سكربت أو flow جدولة واحدة أو أكثر بصيغة cron ذات الحقول الخمسة، مع حقل اختياري للثواني في البداية. معالج أخطاء الجدولة ميزة من إصدار Enterprise؛ أما في Community Edition فراقب التنفيذات الفاشلة من قائمة التشغيلات.
  • Webhooks: يُستدعى السكربت بشكل غير متزامن عبر /api/w/<workspace>/jobs/run/p/<path> (الرد: معرّف المهمة) أو بشكل متزامن عبر /api/w/<workspace>/jobs/run_wait_result/p/<path> (الرد: النتيجة). صادِق بالترويسة Authorization: Bearer <token> بدلًا من ?token= الذي ينتهي في سجلّات الوصول.
  • Git Sync: من إعدادات مساحة العمل، اربط مستودعًا لدفع السكربتات والـ flows والموارد إليه عند كل نشر. مجاني في Community Edition حتى مستخدمَين لكل مساحة عمل؛ أما النشر التلقائي انطلاقًا من Git فمحجوز لإصدار Enterprise.

مثال: سكربت Python من البداية إلى النهاية

يستعلم هذا السكربت واجهة API، ويحتفظ بالعناصر النشطة، ويدرجها في PostgreSQL عبر مورد من Windmill. أنشئ سكربت Python والصق الشيفرة وشغّله من المحرر:

# requirements:
# httpx
# psycopg2-binary
from typing import TypedDict

import httpx
import psycopg2


class postgresql(TypedDict):
    host: str
    port: int
    user: str
    dbname: str
    sslmode: str
    password: str


def main(api_url: str, db: postgresql) -> dict:
    resp = httpx.get(api_url, timeout=10)
    resp.raise_for_status()
    rows = [r for r in resp.json() if r.get("active")]
    conn = psycopg2.connect(
        host=db["host"], port=db["port"], user=db["user"],
        password=db["password"], dbname=db["dbname"], sslmode=db["sslmode"],
    )
    with conn, conn.cursor() as cur:
        cur.executemany(
            "INSERT INTO items (id, name) VALUES (%(id)s, %(name)s) ON CONFLICT DO NOTHING",
            rows,
        )
    conn.close()
    return {"candidates": len(rows), "source": api_url}

تحلّ تعليقات # requirements: محل الاستنتاج التلقائي للاعتماديات من أوامر الاستيراد: وهذا مفيد هنا، لأن الاستيراد psycopg2 يُنشر تحت الحزمة psycopg2-binary. يحلّ Windmill هذه الحزم، ويكتب ملف قفل (lockfile)، ويخزّنها مؤقتًا على العامل، فتعمل النسخة نفسها من السكربت دائمًا بالاعتماديات نفسها. أما الصنف postgresql، المسمّى باسم نوع المورد، فيُظهر في النموذج أداة لاختيار الموارد. بعد نشره تحت المسار f/etl/import_items، يمكن استدعاء السكربت من أي أداة:

curl -X POST "https://windmill.example.com/api/w/prod/jobs/run_wait_result/p/f/etl/import_items" \
  -H "Authorization: Bearer $WM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"api_url": "https://api.example.com/items"}'

تقوية النسخة

لا تنشر أبدًا المنفذين 8000 و3000 على الواجهة العامة: يجب ألا يصل إليهما إلا الوكيل العكسي محليًا. خذ نسخة احتياطية من القاعدة بالأمر docker compose exec -T db pg_dumpall -U postgres | gzip > /backups/windmill-$(date +%F).sql.gz: يجب أن يغطي التفريغ العنقود (cluster) كاملًا لا قاعدة windmill وحدها، لأن Windmill ينشئ قواعد بيانات ملحقة ويربط سياسات الأمان الخاصة به بأدوار على مستوى العنقود. يحتوي هذا التفريغ على أسرارك المشفّرة: خزّنه خارج الخادم واحمِه كما تحمي سرًّا. لا تربط مقبس Docker داخل العمّال إلا إذا كنت تثق بكل مستخدمي النسخة، لأنه يمنح السكربتات السيطرة على المضيف. وأخيرًا، أبقِ REQUIRE_SIGNED_DEBUG_REQUESTS=true على windmill_extra: تعطيله على نسخة يمكن الوصول إليها من الإنترنت يعرض مصحّح أخطاء قادرًا على تنفيذ الشيفرة دون مصادقة.

استكشاف الأعطال: أخطاء النشر الأول

تأتي معظم الأعطال من قاعدة البيانات، أو ذاكرة العمّال، أو الوكيل العكسي. إليك الأعراض وطرق إصلاحها:

الأعراض وطرق الإصلاح

  • تتوقف الحاوية db فور التحديث: أُنشئ الحجم بواسطة PostgreSQL 16 وانتقلت الصورة إلى الإصدار 18 الذي يرفض قراءته. اتبع إجراء الترحيل الموجود في التوثيق (تفريغ كامل ثم استعادة)، أو ثبّت postgres:16 مع نقطة الربط القديمة: يبقى الإصدار 16 مدعومًا حتى نوفمبر 2028.
  • مهمة متوقفة بالرمز 137: تجاوزت حد ذاكرة عاملها. ارفع resources.limits.memory في الخدمة windmill_worker، أو قلّل حجم البيانات التي يحمّلها السكربت في الذاكرة.
  • خادم أو عمّال عاجزون عن الوصول إلى القاعدة: يشير DATABASE_URL إلى localhost بدلًا من الخدمة db، أو تختلف كلمة المرور عن POSTGRES_PASSWORD. أصلح ملف .env ثم نفّذ docker compose up -d لإعادة إنشاء الحاويات.
  • ModuleNotFoundError أو فشل تثبيت الاعتماديات: ينزّل العامل الحزم عند التنفيذ الأول ويجب أن يصل إلى pypi.org وfiles.pythonhosted.org عبر HTTPS صادر. خلف وكيل مؤسسي، يضبط المتغيران PY_TRUSTED_HOST وPY_INDEX_CERT الثقة؛ ويُعلَن الفهرس الخاص في Instance settings (إصدار Enterprise).
  • محرر بلا إكمال تلقائي ومصحّح أخطاء صامت: لا يوجّه الوكيل العكسي المسارات /ws/ و/ws_mp/ و/ws_debug/ إلى windmill_extra، أو يحذف الترويستين Upgrade وConnection. اعتمد التوجيه الوارد في ملف Caddyfile الرسمي.

Windmill أم n8n أم Temporal

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

المعيارWindmilln8nTemporal
كتابة سير العملسكربتات Python وTypeScript وGo وBash وSQL… تُجمَّع في flowsلوحة عُقد؛ وعقدة Code بلغة JavaScript أو Pythonشيفرة تطبيقية عبر SDK (Go وJava وPython وTypeScript و‎.NET وPHP وRuby وRust)
الترخيصAGPLv3 للنواة؛ Community Edition مجانية مع حصص، وميزات Enterprise بترخيصSustainable Use License (fair-code)، وملفات `.ee` بترخيص تجاريMIT للخادم
الواجهةمحرر ويب، ونماذج وتطبيقات داخلية مولَّدةمحرر مرئي للعُقدواجهة ويب للمراقبة؛ تُكتب الشيفرة خارج الأداة
لمنفرق تكتب الشيفرة وتريد عرضها بسرعةتكاملات مرئية بين خدمات SaaSعمليات طويلة بمتطلبات متانة عالية

‏نشرها بنقرة واحدة من Marketplace ServOrbit. إن كنت تفضّل تجاوز الإعداد اليدوي، يوفّر Marketplace من ServOrbit Windmill تلقائيًا — الخادم وعاملان وPostgreSQL 16 ووكيل nginx عكسي وشهادة Let's Encrypt — في بضع دقائق. يعرض قسم «الوصول الأول» في منطقة عملائك الرابطَ والبيانات المولَّدة. تتيح لك بطاقة Windmill، المتاحة من الزر في نهاية هذا المقال، البدء فورًا.

‏انشر Windmill بنقرة واحدة على VPS من ServOrbit

‏يوفّر Marketplace من ServOrbit Windmill تلقائيًا مع PostgreSQL 16 وعاملَين ووكيل عكسي — تعمل نصوصك البرمجية في الإنتاج خلال دقائق، دون أي إعداد.