SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor

علت قطع شدن مداوم n8n روی سرور مجازی (VPS)

اگر n8n مدام آفلاین می‌شود، لزوما مشکل از سرور نیست. با بررسی لاگ‌ها و دستور dmesg، تفاوت خطای OOM Kill، حلقه ریستارت، مشکلات WebSocket و توقف زمان‌بندی‌ها را شناسایی کنید.

چرا n8n مدام آفلاین می‌شود: چهار خرابی، یک نشانه

عبارت «n8n مدام آفلاین می‌شود» یک جمله است که چهار خرابی متفاوت را پوشش می‌دهد و هر کدام به راه‌حل متفاوتی نیاز دارد. ویرایشگر در حالی که container به‌طور عادی در حال اجراست، بنر قطع اتصال را نشان می‌دهد. container خودبه‌خود restart می‌شود. هسته سیستم‌عامل (kernel)، پردازش Node.js را به دلیل مصرف بیش از حد حافظه می‌کشد (kill می‌کند). یا اصلاً هیچ مشکلی در پردازش وجود ندارد و یک workflow فعال هرگز اجرا نمی‌شود. اگر تنظیمات اشتباهی را تغییر دهید، آخر هفته خود را صرف حل مشکلی خواهید کرد که هرگز وجود نداشته است.

بنابراین، پیش از آنکه به سراغ هرگونه پیکربندی بروید، مشخص کنید با کدام خرابی مواجه هستید. n8n به‌عنوان یک پردازش واحد Node.js، معمولاً درون یک Docker container و پشت یک reverse proxy که TLS (امنیت لایه انتقال) را مدیریت می‌کند، اجرا می‌شود. هر یک از این لایه‌ها به شیوه خاص خود دچار اختلال می‌شوند و مرورگر همه آن‌ها را با یک پیام مشابه گزارش می‌دهد.

عیب‌یابی به این ترتیب

این دستورات را روی VPS (سرور مجازی) اجرا کنید و مقادیری را که سیستم خودتان چاپ می‌کند، بخوانید. آن‌ها را با اعداد موجود در انجمن‌های گفتگو مقایسه نکنید. مقادیر مهم در اینجا، وضعیت سرور شما را توصیف می‌کنند، نه سرور شخص دیگری را.

docker ps -a --filter name=n8n
docker logs --tail 200 --timestamps n8n
docker inspect n8n | grep -iE 'Status|Running|RestartCount|OOMKilled|ExitCode'
docker stats --no-stream

ستون STATUS از خروجی docker ps -a نشان می‌دهد که کانتینر چه مدت در وضعیت فعلی خود بوده است. آن را با لحظه‌ای که مشکل شما شروع شده مقایسه کنید. اگر کانتینر از مدت‌ها قبل از ظاهر شدن بنر خطا بالا بوده است، پس n8n هرگز آفلاین نشده است. آنچه از کار افتاده، ارتباط بین مرورگر شما و بک‌اند است که همان مسیر websocket است که در بخش بعدی پوشش داده شده است.

RestartCount نشان‌دهنده تعداد دفعاتی است که Docker این کانتینر را ری‌استارت کرده است. عدد را یادداشت کنید، یک دقیقه صبر کنید و دوباره آن را بخوانید. عددی که در حین مشاهده شما افزایش می‌یابد، نشان‌دهنده یک حلقه ری‌استارت (restart loop) است و خطوط لاگ درست قبل از هر ری‌استارت، دلیل آن را در خود دارند.

OOMKilled یک پرچم (flag) درست یا نادرست است. مقدار True به این معنی است که هسته لینوکس (Linux kernel) فرآیند را به دلیل عبور از محدودیت حافظه متوقف کرده است؛ این محدودیت می‌تواند مربوط به خود کانتینر یا کل ماشین باشد. همین یک فیلد، توقف به دلیل کمبود حافظه (memory kill) را از سایر انواع خروج متمایز می‌کند، به همین دلیل است که پیش از حدس زدن، باید آن را بررسی کنید.

ExitCode نشان‌دهنده کد خروج آخرین وضعیت کانتینر است. نیازی نیست معنی هر کد را حفظ کنید. کد خود را بخوانید و سپس انتهای docker logs را در همان بازه زمانی بررسی کنید. انتهای لاگ و پرچم کمبود حافظه در کنار هم به شما می‌گویند چه اتفاقی افتاده است؛ تکیه بر هر کدام از آن‌ها به تنهایی می‌تواند شما را گمراه کند.

docker stats میزان مصرف لحظه‌ای حافظه را در کنار محدودیت اعمال‌شده نشان می‌دهد. بگذارید این دستور در ترمینال دوم اجرا شود، سپس workflowای که باعث خرابی می‌شود را اجرا کنید و ببینید در حین وقوع خطا، این عدد چه تغییری می‌کند.


بنر قطع اتصال معمولاً مربوط به reverse proxy شماست

ویرایشگر n8n یک اتصال طولانی‌مدت با backend باز نگه می‌دارد تا بتواند پیشرفت اجرا را روی صفحه نمایش (canvas) استریم کند. به‌طور پیش‌فرض، این اتصال یک WebSocket است که توسط N8N_PUSH_BACKEND انتخاب می‌شود و مقدار پیش‌فرض آن websocket است. یک WebSocket به عنوان یک درخواست HTTP معمولی شروع می‌شود که شامل هدرهای Connection: Upgrade و Upgrade: websocket است. سرور پاسخ 101 Switching Protocols را ارسال می‌کند و از آن لحظه به بعد، هر دو طرف از همان سوکت TCP در هر دو جهت استفاده می‌کنند.

دو عامل باعث اختلال در این فرآیند می‌شوند و هر دو در proxy قرار دارند، نه در n8n. یا proxy با پروتکل HTTP/1.0 به سمت upstream صحبت می‌کند یا هدرهای upgrade را حذف می‌کند؛ در نتیجه ارتقا (upgrade) هرگز انجام نمی‌شود و ویرایشگر دائماً در حال تلاش برای اتصال مجدد است. یا ارتقا با موفقیت انجام می‌شود اما proxy بعداً سوکت را به دلیل عدم فعالیت می‌بندد، زیرا یک WebSocket بدون پیام دقیقاً مانند یک اتصال بیکار (idle) به نظر می‌رسد. در هر دو حالت، container سالم است. این بنر در واقع مرورگر است که به شما اطلاع می‌دهد کانال ارتباطی خود را از دست داده است.

پیش از تغییر هر تنظیمی، این موضوع را در مرورگر تأیید کنید. ابزارهای توسعه‌دهنده (developer tools) را باز کنید، به تب Network بروید، فیلتر را روی WS تنظیم کنید و ویرایشگر را reload کنید. درخواست push باید به 101 Switching Protocols برسد و باز بماند. درخواست push که یک کد وضعیت معمولی برمی‌گرداند یا هر چند ثانیه یک‌بار دوباره ظاهر می‌شود، نشان‌دهنده وجود مشکل در proxy است.

تنظیمات nginx برای حفظ اتصال ویرایشگر

nginx تا زمانی که از آن نخواهید، درخواست‌های upgrade را ارسال نمی‌کند. proxy_pass به‌صورت پیش‌فرض با پروتکل HTTP/1.0 با backend صحبت می‌کند و Connection و Upgrade هدرهای hop-by-hop هستند که nginx آن‌ها را در مسیر عبور حذف می‌کند. شما باید هر دو را دوباره اضافه کنید. بلوک map باید در context مربوط به http قرار گیرد، نه داخل server.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
server {
    listen 443 ssl;
    http2 on;
    server_name n8n.example.com;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
}

proxy_read_timeout خطی است که معمولاً فراموش می‌شود. مقدار پیش‌فرض آن 60 ثانیه است و برای WebSocketهای ارتقایافته نیز اعمال می‌شود؛ بنابراین اگر تب ویرایشگر روی یک نمونه خلوت باز بماند، اتصال آن حدود یک دقیقه پس از آخرین پیام ارسالی قطع می‌شود. افزایش این مقدار، مشکل بنری که هنگام بازگشت به تب بازشده مشاهده می‌کنید را حل می‌کند.

sudo nginx -t && sudo systemctl reload nginx
sudo nginx -T | grep -iE 'proxy_http_version|upgrade|proxy_read_timeout'

nginx -T کل پیکربندی در حال اجرا را به‌جای یک فایل واحد چاپ می‌کند، بنابراین ثابت می‌کند که ویرایش شما واقعاً بارگذاری شده است. پیکربندی‌ای که در فایلی قرار دارد که هیچ خط include آن را فراخوانی نمی‌کند، دلیلی است که باعث می‌شود یک اصلاح صحیح، بی‌اثر به نظر برسد.

سپس به n8n اطلاع دهید که پشت یک proxy قرار دارد، زیرا این برنامه URLها را بر اساس این مقادیر می‌سازد.

environment:
  - N8N_HOST=n8n.example.com
  - N8N_PROTOCOL=https
  - N8N_PORT=5678
  - N8N_PROXY_HOPS=1
  - N8N_WEBHOOK_URL=https://n8n.example.com/

N8N_PROXY_HOPS به‌صورت پیش‌فرض 0 است، به این معنی که n8n آدرس متصل‌شونده را به‌عنوان آدرس کلاینت در نظر می‌گیرد و X-Forwarded-For را نادیده می‌گیرد. آن را روی تعداد proxyهای موجود در مقابل container تنظیم کنید. از اوت 2026، N8N_WEBHOOK_URL نام فعلی است و WEBHOOK_URL قدیمی‌تر همچنان کار می‌کند، اگرچه هنگام راه‌اندازی یک هشدار deprecation چاپ می‌کند.

Traefik درخواست‌های WebSocket را هدایت می‌کند، اما آن‌ها را با timeout مواجه می‌سازد

Traefik درخواست‌های ارتقای WebSocket را بدون نیاز به middleware یا برچسب‌های اضافی هدایت می‌کند؛ بنابراین کاربری که در Traefik با این خطا مواجه می‌شود، معمولاً با مشکل timeout روبروست، نه فقدان هدر. تنظیمات مربوط به این مورد در entryPoint قرار دارند. تا اوت 2026 در نسخه Traefik v3، مقدار پیش‌فرض برای idleTimeout برابر با 180 ثانیه و برای readTimeout برابر با 60 ثانیه است.

entryPoints:
  websecure:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: 0
        idleTimeout: 3600s

Caddy عملیات ارتقا را به‌صورت خودکار در reverse_proxy مدیریت می‌کند و نیازی به دستورالعمل خاصی برای آن ندارد. اگر به دلیل اینکه مدیریت proxy در اختیار شخص دیگری است، امکان تغییر آن را ندارید، کانال push را با N8N_PUSH_BACKEND=sse تغییر دهید. پروتکل SSE (رویدادهای ارسالی از سمت سرور) یک پاسخ HTTP معمولی است که باز نگه داشته می‌شود، بنابراین از proxyهایی که ارتقا را نمی‌پذیرند عبور می‌کند، هرچند یک timeout غیرفعال (idle timeout) تهاجمی همچنان می‌تواند آن را قطع کند. انتخاب خودِ proxy یک تصمیم جداگانه است و مقایسه nginx، Caddy و Traefik هزینه‌های عملیاتی هرکدام را بررسی می‌کند.

هنگامی که کانتینر واقعاً در حال ری‌استارت شدن است

اگر RestartCount افزایش می‌یابد، کانتینر با شکست مواجه شده و Docker در حال راه‌اندازی مجدد آن است. زمان‌بندی‌های لاگ را با هر ری‌استارت تطبیق دهید و آنچه بلافاصله پیش از آن رخ داده است را بخوانید. چهار علت تقریباً تمام موارد را پوشش می‌دهند: خطای پیکربندی که مانع از شروع به کار می‌شود، دیتابیسی که n8n به آن دسترسی ندارد، کرش کردن پس از اجرا، و کشته شدن توسط حافظه (OOM Kill).

با volume شروع کنید، زیرا مجوزها (permissions) عامل پنهانی هستند. ایمیج رسمی با کاربر بدون دسترسی ویژه node اجرا می‌شود و داده‌های خود را در /home/node/.n8n نگه می‌دارد. یک bind mount که توسط root ایجاد شده باشد، برای آن کاربر قابل نوشتن نیست؛ بنابراین فرآیند هر بار در هنگام شروع متوقف می‌شود و سیاست ری‌استارت (restart policy)، این موضوع را در یک حلقه پنهان می‌کند.

docker compose config
docker run --rm -it --entrypoint sh docker.n8n.io/n8nio/n8n -c 'id'
docker exec n8n ls -ld /home/node/.n8n

یک named volume این مشکل را به‌طور کامل برطرف می‌کند، زیرا Docker آن را با مالکیت صحیح ایجاد می‌کند. اگر به bind mount نیاز دارید، دایرکتوری میزبان را با استفاده از chown به شناسه کاربری عددی که دستور اول چاپ کرده است، تغییر مالکیت دهید. درک نگاشت مالکیت بین میزبان و کانتینر یک بار برای همیشه مفید است و توضیح‌دهنده PUID و PGID بررسی می‌کند که این ایمیج‌ها چگونه تصمیم می‌گیرند چه کسی فایل‌ها را بنویسد.

خروج از حافظه (OOM Kill) که شبیه به کرش است

دو سقف حافظه مجزا برای یک پردازش n8n وجود دارد که رفتارهای متفاوتی هنگام خطا نشان می‌دهند. محدودیت گروه کنترل (cgroup) کانتینر توسط هسته سیستم‌عامل اعمال می‌شود: اگر از آن عبور کنید، پردازش بلافاصله کشته می‌شود، بدون اینکه فرصتی برای نوشتن چیزی داشته باشد و OOMKilled مقدار true را برمی‌گرداند. محدودیت V8 heap در داخل Node.js اعمال می‌شود: اگر از آن عبور کنید، Node یک خطای heap همراه با stack trace ایجاد کرده و خودش خارج می‌شود، بنابراین OOMKilled مقدار false را برمی‌گرداند. از دید مرورگر، این دو مورد یکسان به نظر می‌رسند. اما از دید docker inspect، آن‌ها تنها یک فیلد با هم تفاوت دارند.

سقف حافظه Node heap را پایین‌تر از محدودیت کانتینر تنظیم کنید. اگر سقف heap بالاتر از محدودیت کانتینر باشد، V8 به تخصیص حافظه فراتر از نقطه‌ای که هسته مداخله می‌کند ادامه می‌دهد، بنابراین garbage collector هرگز به محدودیت خود نمی‌رسد و شما همیشه با سخت‌ترین نوع شکست مواجه می‌شوید که هیچ لاگی برای خواندن باقی نمی‌گذارد.

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    environment:
      - NODE_OPTIONS=--max-old-space-size=<MiB, below the container limit>
    deploy:
      resources:
        limits:
          memory: <your container limit>

هر دو عدد را بر اساس ظرفیت واقعی VPS خود انتخاب کنید و فضایی را برای دیتابیس، پروکسی و سیستم‌عامل در نظر بگیرید. دستور docker stats --no-stream میزان استفاده فعلی را در کنار محدودیت اعمال‌شده چاپ می‌کند، بنابراین می‌توانید بررسی کنید که محدودیتی که نوشتید همان محدودیتی است که Docker اعمال کرده است. نحوه اعمال محدودیت‌های حافظه در Compose توضیح می‌دهد که وقتی چندین محدودیت تنظیم شده باشد، کدام کلید اولویت دارد.

داده‌های اجرا همان چیزی است که در زیر پای شما رشد می‌کند

یک اجرای واحد، خروجی تمام گره‌ها را در حین انجام کار نگه می‌دارد و n8n سپس آن داده‌ها را ذخیره می‌کند. این موضوع دو پیامد دارد. اوج مصرف حافظه در یک اجرا توسط بزرگ‌ترین دسته‌ای از داده‌ها تعیین می‌شود که از آن عبور می‌دهید؛ بنابراین، گردش‌کاری که ده هزار ردیف را به‌طور هم‌زمان پردازش می‌کند، برنامه‌ای متفاوت از همان گردش‌کار است که دویست ردیف را در هر مرحله پردازش می‌کند. همچنین، نسخه ذخیره‌شده تا زمانی که چیزی آن را حذف نکند، به رشد خود ادامه می‌دهد.

هرس‌کردن (Pruning) مشکل دوم را حل می‌کند. از اوت 2026، تنظیمات پیش‌فرض به این صورت است که هرس‌کردن فعال است، EXECUTIONS_DATA_MAX_AGE روی 336 ساعت (14 روز) و EXECUTIONS_DATA_PRUNE_MAX_COUNT روی 10000 تنظیم شده است. این مقادیر برای یک VPS کوچک که از SQLite استفاده می‌کند سخاوتمندانه هستند؛ جایی که یک فایل همه چیز را در خود نگه می‌دارد و همان فرآیندی که ویرایشگر را سرویس‌دهی می‌کند، باید آن را بخواند و بنویسد.

environment:
  - EXECUTIONS_DATA_PRUNE=true
  - EXECUTIONS_DATA_MAX_AGE=72
  - EXECUTIONS_DATA_PRUNE_MAX_COUNT=1000
  - EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
  - EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=false

EXECUTIONS_DATA_SAVE_ON_SUCCESS=none تنظیم تهاجمی است. این گزینه اجراهای ناموفق را برای عیب‌یابی نگه می‌دارد و اجراهای موفق را دور می‌ریزد. این تصمیم را آگاهانه بگیرید، زیرا گردش‌کاری که بدون ایجاد خطا، خروجی اشتباه تولید کرده است، دیگر چیزی برای بررسی باقی نمی‌گذارد. هرس‌کردن همچنین ابتدا ردیف‌ها را به‌عنوان حذف‌شده علامت‌گذاری می‌کند و در مرحله‌ای بعدی آن‌ها را پاک می‌کند، و از آنجا که SQLite صفحات آزادشده را به‌جای بازگرداندن به سیستم‌عامل، دوباره استفاده می‌کند، حجم فایل روی دیسک بلافاصله پس از تغییر تنظیمات کاهش نمی‌یابد.

برای کاهش اوج مصرف به‌جای مجموع داده‌های ذخیره‌شده، در هر اجرا داده‌های کمتری جابه‌جا کنید. کارهای بزرگ را به زیر-گردش‌کارهایی تقسیم کنید که نتایج کوچکی را به والد بازمی‌گردانند، با استفاده از گره Loop Over Items دسته‌بندی کنید و کل مجموعه‌داده‌ها را از گره Code دور نگه دارید.

فایل‌های باینری نباید از طریق حافظه منتقل شوند

N8N_DEFAULT_BINARY_DATA_MODE به‌صورت پیش‌فرض روی default تنظیم شده است که داده‌های باینری را در حافظهٔ پردازش در حال اجرا نگه می‌دارد. هر فایلی که یک نود دانلود می‌کند و هر نسخه‌ای که به نود بعدی تحویل داده می‌شود، تا پایان اجرای جریان کاری در حافظه باقی می‌ماند. یک جریان کاری که چند پیوست بزرگ را دریافت می‌کند، می‌تواند مصرف حافظهٔ پردازش را از حد مجاز فراتر ببرد؛ حدی که پردازش‌های معمولی JSON هرگز به آن نزدیک نمی‌شوند. به همین دلیل است که کرش کردن برنامه به یک جریان کاری خاص وابسته است، نه به زمان اجرا.

environment:
  - N8N_DEFAULT_BINARY_DATA_MODE=filesystem

با استفاده از filesystem، داده‌های باینری در مسیر N8N_BINARY_DATA_STORAGE_PATH نوشته می‌شوند که به‌صورت پیش‌فرض در پوشهٔ کاربری n8n قرار دارد و در نتیجه روی همان volume سایر داده‌ها ذخیره می‌شود. پیش از تغییر این تنظیم، اطمینان حاصل کنید که volume فضای کافی دارد. N8N_PAYLOAD_SIZE_MAX حداکثر اندازهٔ payload وب‌هوک ورودی را بر حسب MiB (مبی‌بایت) تعیین می‌کند و مقدار پیش‌فرض آن 16 است. افزایش این مقدار به درخواست‌های بزرگ‌تر اجازهٔ ورود می‌دهد که به معنای پذیرش هزینهٔ مصرف حافظهٔ بیشتر است.

هر سرویس دیگری که روی سرور اجرا می‌شود، برای استفاده از RAM با این پردازش رقابت می‌کند. اگر خطاهای OOM (کمبود حافظه) دقیقاً پس از اضافه کردن یک کانتینر دیتابیس شروع شده‌اند، اجرای دیتابیس در Docker یا روی سیستم‌عامل میزبان همان موازنه‌ای است که اکنون باید در نظر بگیرید.

سیاست راه‌اندازی مجدد و بازگشت پس از reboot

کانتینری که فاقد سیاست راه‌اندازی مجدد (restart policy) باشد، پس از خروج و همچنین پس از reboot شدن میزبان، متوقف باقی می‌ماند. دستور restart: unless-stopped آن را در هر دو حالت بازمی‌گرداند، در حالی که همچنان به کانتینری که به‌صورت دستی متوقف کرده‌اید احترام می‌گذارد. دستور restart: always حتی کانتینری را که عمداً متوقف کرده‌اید، پس از راه‌اندازی مجدد Docker دوباره اجرا می‌کند.

سرویس n8n یک endpoint سلامت ارائه می‌دهد که با N8N_ENDPOINT_HEALTH نام‌گذاری شده و مقدار پیش‌فرض آن healthz است. ابتدا آن را از روی میزبان بررسی کنید تا مطمئن شوید مسیر در نمونه (instance) شما صحیح است.

curl -fsS http://127.0.0.1:5678/healthz
docker exec n8n which wget curl
sudo systemctl is-enabled docker

یک healthcheck به‌تنهایی هیچ‌چیزی را راه‌اندازی مجدد نمی‌کند. Compose کانتینر را در وضعیت ناسالم (unhealthy) علامت‌گذاری کرده و در همان‌جا متوقف می‌شود؛ بنابراین برای اینکه healthcheck تأثیری داشته باشد، به یک سیاست راه‌اندازی مجدد یا یک ناظر خارجی در کنار خود نیاز دارد. نوشتن یک healthcheck که واقعاً عمل کند و راه‌اندازی مجدد stack پس از reboot هر دو بخش این موضوع را پوشش می‌دهند.

گردش‌کاری که اجرا نمی‌شود در حالی که n8n سالم است

این وضعیت هیچ بنری نمایش نمی‌دهد و باعث راه‌اندازی مجدد نمی‌شود. کانتینر در حال اجراست، ویرایشگر کار می‌کند، اما اجرای مورد انتظار شما در لیست executions دیده نمی‌شود. چهار دلیل عمده برای این مشکل وجود دارد:

  • گردش‌کار فعال نیست. یک Schedule Trigger فقط در مسیر production اجرا می‌شود، بنابراین تست کردن آن در محیط canvas هیچ زمان‌بندی‌ای ایجاد نمی‌کند.
  • منطقه زمانی (timezone) تنظیم‌شده با منطقه شما متفاوت است. مقدار پیش‌فرض GENERIC_TIMEZONE برابر با America/New_York است، بنابراین زمان‌بندی‌ای که برای 09:00 تنظیم شده، تا زمانی که GENERIC_TIMEZONE و TZ را به منطقه زمانی خود تغییر ندهید، در همان منطقه زمانی پیش‌فرض اجرا می‌شود.
  • زمان‌های از دست رفته (downtime) بعداً جبران نمی‌شوند. تریگرها هنگام شروع به کار n8n ثبت می‌شوند، بنابراین زمان‌بندی‌ای که در حین راه‌اندازی مجدد کانتینر سررسید شده باشد، با تأخیر اجرا نمی‌شود. اجرای بعدی، اولین زمان سررسید پس از بالا آمدن سیستم خواهد بود.
  • گردش‌کار به‌طور خودکار غیرفعال شده است. گزینه N8N_WORKFLOW_AUTODEACTIVATION_ENABLED به‌صورت پیش‌فرض خاموش است؛ وقتی این گزینه روشن باشد، گردش‌کاری که مدام دچار کرش شود غیرفعال (unpublished) می‌شود و پس از آن دقیقاً مشابه گردش‌کاری به نظر می‌رسد که هیچ‌کس آن را فعال نکرده است.

لیست executions را باز کنید و آن را بر اساس گردش‌کار مورد نظر فیلتر کنید. وجود یک ورودی که وضعیت failed دارد، نشان‌دهنده مشکل در خود گردش‌کار است. اگر هیچ ورودی‌ای وجود ندارد، مشکل از تریگر است و باید چهار مورد ذکر شده در بالا را بررسی کنید.

چه چیزی را ابتدا تغییر دهیم

  1. پیش از ویرایش هر فایلی، STATUS، RestartCount و OOMKilled را روی کانتینر خود مطالعه کنید.
  2. اگر کانتینر هرگز از دسترس خارج نشده است، هدرهای ارتقای پروکسی (proxy upgrade headers) و مهلت زمانی بیکاری (idle timeout) را اصلاح کنید.
  3. اگر OOMKilled برابر با true است، محدودیتی که آگاهانه برای کانتینر انتخاب کرده‌اید اعمال کنید، سقف حافظه heap در Node را پایین‌تر از آن قرار دهید و داده‌های باینری را به filesystem تغییر دهید.
  4. اگر هیچ موردی فعال نشد، بررسی کنید که گردش کار (workflow) فعال باشد و منطقه زمانی نمونه (instance timezone) با منطقه زمانی شما مطابقت داشته باشد.

بیشتر این موارد تنظیماتی هستند که پس از یک نصب موفق، یک‌بار اعمال می‌شوند و دیگر نیازی به تغییر ندارند. اگر هنوز در حال انجام مراحل نصب هستید، راهنمای نصب n8n روی Docker با HTTPS همان پایه‌ای است که این تنظیمات به آن تعلق دارند.

FAQ

چرا ویرایشگر n8n با وجود در حال اجرا بودن کانتینر، بنر connection lost را نمایش می‌دهد؟

ویرایشگر برای استریم کردن پیشرفت اجرا، یک اتصال WebSocket باز نگه می‌دارد. اگر reverse proxy شما هدرهای Connection: Upgrade و Upgrade: websocket را ارسال نکند یا از HTTP/1.1 در سمت upstream استفاده نکند، عملیات upgrade هرگز تکمیل نمی‌شود و مرورگر به‌طور مداوم در حال تلاش برای اتصال مجدد باقی می‌ماند، در حالی که n8n سالم است. در nginx شما به proxy_http_version 1.1 به همراه هر دو خط proxy_set_header و همچنین یک proxy_read_timeout طولانی‌تر از مقدار پیش‌فرض 60 ثانیه نیاز دارید تا تب‌های غیرفعال قطع نشوند. پیکربندی در حال اجرا را با sudo nginx -T بررسی کنید، نه فایلی که ویرایش کرده‌اید.

چگونه می‌توانم تفاوت بین OOM kill (کشته شدن توسط سیستم به دلیل کمبود حافظه) و یک کرش معمولی را تشخیص دهم؟

دستور docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount' را اجرا کرده و مقدار فلگ OOMKilled را بخوانید. مقدار True به این معناست که هسته سیستم‌عامل (kernel) فرآیند را به دلیل عبور از محدودیت حافظه کشته است و در لاگ کانتینر چیزی مفیدی وجود نخواهد داشت، زیرا فرآیند فرصتی برای نوشتن نداشته است. مقدار False، همراه با خطای heap و stack trace در انتهای docker logs، به این معناست که Node.js به سقف V8 heap خود رسیده و به‌طور خودکار خارج شده است. مقدار NODE_OPTIONS=--max-old-space-size را کمتر از محدودیت کانتینر خود تنظیم کنید تا با خطای دوم مواجه شوید؛ این همان خطایی است که شواهد آن باقی می‌ماند.

آیا پاک‌سازی داده‌های اجرا (pruning)، فضای دیسک را بلافاصله آزاد می‌کند؟

خیر. EXECUTIONS_DATA_PRUNE اجراهای قدیمی را برای حذف علامت‌گذاری می‌کند و یک پردازش در زمان بعدی، آن‌ها را طبق زمان‌بندی تعیین‌شده توسط EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL حذف می‌کند. در SQLite، فایل مربوطه به‌جای بازگرداندن فضا به سیستم‌فایل، صفحات آزاد شده را مجدداً استفاده می‌کند، بنابراین حجم فایل روی دیسک تا مدتی پس از حذف ردیف‌ها ثابت می‌ماند. مقادیر EXECUTIONS_DATA_MAX_AGE و EXECUTIONS_DATA_PRUNE_MAX_COUNT را متناسب با سرور خود تنظیم کنید و سپس به‌جای بررسی فوری، روز بعد وضعیت را چک کنید.

چرا workflow زمان‌بندی‌شده من در حین restart شدن n8n اجرا نشد؟

n8n تریگرها را هنگام شروع فرآیند ثبت می‌کند و زمان‌بندی‌هایی که در زمان خاموش بودن سیستم سررسید شده‌اند را دوباره اجرا نمی‌کند. بنابراین، یک حلقه restart باعث سکوت می‌شود و نه اجرای انبوه کارهای عقب‌افتاده؛ اجرای بعدی در اولین زمان سررسید پس از startup انجام خواهد شد. اگر به اجراهایی نیاز دارید که نباید از دست بروند، workflow را از طریق یک فراخوان خارجی که به webhook متصل می‌شود هدایت کنید تا منطق retry خارج از n8n قرار گیرد.

آیا healthcheck در صورت عدم پاسخگویی n8n، آن را restart می‌کند؟

به‌تنهایی خیر. یک healthcheck در Compose فقط وضعیت کانتینر را به عنوان سالم یا ناسالم علامت‌گذاری می‌کند. restart کردن وظیفه restart policy است، بنابراین restart: unless-stopped همان چیزی است که کانتینر را پس از خروج به حالت اجرا برمی‌گرداند و همچنین پس از reboot میزبان، تا زمانی که سرویس Docker فعال باشد، آن را بالا می‌آورد. این موضوع را با sudo systemctl is-enabled docker تأیید کنید. برای اقدام خاص روی وضعیت ناسالم (unhealthy)، به یک ناظر (watcher) خارج از Docker نیاز دارید که وضعیت را بخواند و سرویس را restart کند.