SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-10-06

تشغيل Claude Code على VPS بعيد باستخدام tmux

شغّل Claude Code على خادم Linux دائم التشغيل داخل tmux لتبقى جلسات agent بعد انقطاع SSH. تعرّف إلى التثبيت والتحصين وأخطاء SIGHUP المتوقعة.

المشكلة في غطاء الحاسوب المحمول، لا في CLI

يعمل Claude Code بشكل طبيعي على حاسوبك المحمول إلى أن تغلقه: تنقطع جلسة SSH، وتحصل الصدفة على SIGHUP، ويتوقف agent معها بعد ثلاث دقائق من بدء الاختبار. شغّل CLI على جهاز لا يدخل في وضع السكون، داخل terminal multiplexer لا تكون عملياته أبناءً لجلسة SSH. هذه هي الحيلة كلها. ويُعد tmux الجزء الأساسي، لا عملية التثبيت.

هذه الصفحة مخصّصة لتشغيل جهاز تترك عليه agents قيد التشغيل. إذا لم يكن لديك خادم Linux يمكنك إبقاءه قيد التشغيل، فلن ينطبق أي مما يلي عليك. ذلك هو المتطلب الأساسي الوحيد بصراحة.

ما الذي يفعله tmux فعلياً

عندما تتصل عبر SSH، ينشئ sshd عملية shell فرعية ويوفّر لها pseudo-terminal؛ وكل ما تبدأه من ذلك الـshell يكون عملية فرعية لها. عند انقطاع الاتصال، تنهي النواة عمل pty، وتتلقى shell الإشارة SIGHUP، ثم ترسلها بدورها إلى عملياتها الفرعية. وتموت العمليات طويلة التشغيل التي تعمل في الواجهة الأمامية.

يغيّر tmux علاقة الملكية هذه. الأمر tmux الذي تكتبه هو عميل خفيف يتصل عبر unix socket بخادم tmux server يعمل بصورة منفصلة عن الطرفية. وتكون الـshells داخل الجلسة عمليات فرعية لذلك الخادم، لا لـ sshd. عند إنهاء اتصال SSH، يتوقف العميل بينما يواصل الخادم والجلسة وagent الذي ينفّذ المهمة عملها. عند إعادة الاتصال، استخدم tmux attach، وستعود إلى الـshell نفسها مع سجل التمرير نفسه. يواصل nohup العمل بعد انقطاع الاتصال أيضاً، لكنه لا يوفّر طريقة للعودة إلى الجلسة؛ فلا يمكنك إعادة الاتصال بواجهة TUI التي شغّلتها في الخلفية. Claude Code تفاعلي؛ لذلك يُعد tmux أو screen الأداة المناسبة.

تحديد مواصفات الخادم

CLI عبارة عن عملية Node، وليس هو ما يملأ موارد الجهاز. الذي يملأ الجهاز هو كل ما يشغّله agent نيابةً عنك: عملية build، أو مجموعة اختبارات كاملة، أو tsc، أو language server، أو قاعدة بيانات داخل Docker. حدّد المواصفات وفق toolchain، وليس وفق CLI. أضف swap حتى إذا كنت تخطط لعدم استخدامه مطلقاً، فهو يحوّل إنهاء العملية بسبب نفاد الذاكرة OOM إلى build بطيء:

sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

راقب مساحة القرص أيضاً: تتراكم repos وnode_modules وDocker images بسرعة. وإذا امتد toolchain إلى ما بعد الحاويات ليشمل أجهزة افتراضية كاملة، مثل KVM guest أو عقدة Kubernetes محلية، فتحقق من أن الخطة تتيح امتدادات المحاكاة الافتراضية للمعالج قبل الالتزام بها، لأن تشغيل المحاكاة الافتراضية المتداخلة على VPS أمر يفعّله مزود الخدمة نيابةً عنك، وليس إعداداً تفعّله من داخل guest.

مستخدم غير root أولاً

أنشئ مستخدماً مخصصاً مع مجلده الرئيسي الخاص، وضع مفتاحك العام في مكانه:

sudo adduser --disabled-password --gecos "" agent
sudo install -d -m 700 -o agent -g agent /home/agent/.ssh
sudo cp ~/.ssh/authorized_keys /home/agent/.ssh/authorized_keys
sudo chown agent:agent /home/agent/.ssh/authorized_keys
sudo chmod 600 /home/agent/.ssh/authorized_keys

اختبر تسجيل الدخول من طرفية ثانية قبل المتابعة، ما دام توثيق كلمات المرور متاحاً كخيار احتياطي. إذا ظهرت الرسالة تم رفض الإذن (publickey)، فعادةً تكون المشكلة في ملكية الدليل .ssh أو وضعه، لا في المفتاح نفسه.

عمداً، لا ينتمي agent إلى المجموعة sudo. إذا احتجت إلى حزمة نظام، فثبّتها. هذا القرار وحده يزيل معظم الطرق التي يمكن بها لأمر shell عابر أن يتسبب في إتلاف الخادم.

نظافة إعدادات SSH لخادم تتركه قيد التشغيل

مصادقة كلمات المرور على جهاز متصل بالإنترنت العام طوال اليوم، ويحتوي على agent وشفرة المصدر الخاصة بك، مخاطرة لا تستحق تحمّلها. عطّلها. في Ubuntu 24.04 وDebian 13، يتضمن /etc/ssh/sshd_config ‏/etc/ssh/sshd_config.d/*.conf، لذا أنشئ ملفاً بدلاً من تعديل الإعداد الرئيسي:

# /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no

لا تضف إعدادات تبادل المفاتيح إلى هذا الملف إلا لسبب محدد. غالباً ما يفرض سطر KexAlgorithms المنسوخ من دليل قديم لتقوية الإعدادات استخدام خوارزميات تقليدية فقط. عندها قد يعرض عميل ssh حديث تحذيراً يفيد بأن الاتصال لا يستخدم تبادل مفاتيح ما بعد الكم.

تحقق من الإعدادات ثم أعد التحميل. أبقِ جلستك الحالية مفتوحة أثناء اختبار جلسة جديدة من طرفية ثانية:

sudo sshd -t && sudo systemctl restart ssh

توجد نقطة مهمة في Ubuntu 24.04: يعمل sshd بتفعيل قائم على socket. تُطبَّق إعدادات المصادقة على systemctl restart ssh، لكن تغيير Port الذي يستمع إليه يتطلب أيضاً systemctl daemon-reload وإعادة تشغيل ssh.socket.

ثم اضبط الجدار الناري. اسمح باتصالات SSH قبل تفعيله، وإلا ستفقد الوصول إلى الخادم:

sudo ufw allow OpenSSH
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable

ثبّت fail2ban بعد أن تحدد بوضوح ما الذي يقدمه. بعد تعطيل مصادقة كلمات المرور، لن تنجح هجمات القوة الغاشمة على أي حال. لكنه يمنع محاولات الدخول الفاشلة من الوصول إلى journal.

# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd
maxretry = 5
bantime = 1h

أخيراً، فعّل التصحيح التلقائي باستخدام sudo apt install unattended-upgrades وsudo dpkg-reconfigure -plow unattended-upgrades. انتبه إلى تفاعله مع tmux: عند تفعيل Unattended-Upgrade::Automatic-Reboot، تؤدي ترقية النواة إلى إعادة تشغيل الخادم، فتُنهي جميع الجلسات معه. اتركه معطلاً وأعد التشغيل وفق جدولك الخاص، عندما لا تكون هناك عملية قيد التنفيذ. وينطبق التحذير نفسه على ترقية الإصدار: نقل الخادم من Ubuntu 24.04 إلى 26.04 يعيد تشغيل sshd والنواة، لذا نفّذ ذلك ضمن نافذة لا تحتوي على جلسة tmux تشغّل عملاً مهماً.

تثبيت Node.js وClaude Code على Ubuntu

Claude Code هو Node CLI، لذلك تحتاج إلى إصدار حديث من Node. غالباً ما تتأخر حزمة التوزيعة؛ ويُعد NodeSource المسار المعتاد على Ubuntu وDebian، إذ يوفّر مستودعاً موقّعاً (لا تستخدم apt-key، فقد أُزيلت هذه الأداة):

curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
node --version

إليك الخطأ الشائع: ثبّت CLI باستخدام مستخدمك agent، ولا تستخدم sudo npm -g مطلقاً. يؤدي ضبط global prefix المملوك لـroot إلى أخطاء في الصلاحيات لاحقاً، ويترك ملفات مملوكة لـroot في ذاكرة npm المؤقتة. اضبط prefix في npm على مجلد المستخدم الرئيسي أولاً:

mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
npm install -g @anthropic-ai/claude-code
claude --version

ضع التصدير في ~/.bashrc، وليس في ~/.profile، ويجب أن يأتي قبل الحارس "If not running interactively, don't do anything" القريب من أعلى الملف: قد يبدأ tmux أصدافاً غير صالحة لتسجيل الدخول، فتقرأ ~/.bashrc وتتجاوز ~/.profile، بينما يعمل ~/.profile مع أصداف تسجيل الدخول فقط. يحقق استخدام Node خاص بالمستخدم عبر مدير إصدارات مثل nvm النتيجة نفسها؛ والهدف في الحالتين هو ألا يحتاج npm install -g إلى sudo مطلقاً. يظل npm يعمل بشكل جيد، أو استخدم سكربت التثبيت الأصلي من Anthropic، وهو الخيار الافتراضي الموثق حالياً. راجع وثائق التثبيت لدى Anthropic قبل اللصق، لأن طرق التثبيت تتغير.

شغّل claude داخل مستودع لبدء تشغيله. يوجّهك التشغيل الأول خلال عملية المصادقة؛ ولا يحتوي الخادم الذي يعمل دون واجهة رسومية على متصفح، لذلك يعرض لك المسار URL لفتحه على جهازك، ثم يعرض رمزاً تعيده إلى الطرفية. (مفتاح API في البيئة هو المسار الآخر.) في كلتا الحالتين، تصبح بيانات الاعتماد هذه موجودة على الخادم، وهذا يقودنا إلى الجزء الذي يتجاهله كثيرون.

نطاق تأثير الاختراق

الوكيل الذي يملك وصول shell هو shell. يمكنه قراءة كل ما يستطيع المستخدم الذي يعمل باسمه قراءته، والدفع إلى أي مكان يستطيع ذلك المستخدم الدفع إليه. هذا ليس انتقاداً للأداة، بل هو تعريفها. لذلك فإن الحساب الذي تعمل الأداة باسمه أهم من أي إعداد فردي.

  • مستخدم مخصص غير مميّز. لا تمنحه مجموعة sudo، ولا تشاركه الدليل المنزلي مع حسابك.
  • لا تضع بيانات اعتماد الإنتاج على الخادم. لا يوجد ~/.aws/credentials يحتفظ بمفاتيح الإنتاج، ولا .env منسوخ من بيئة الإنتاج، ولا كلمة مرور قاعدة بيانات تملك صلاحية الكتابة إلى أي مورد مهم. امنح الوكيل بيانات اعتماد لبيئة التجهيز أو بيانات اعتماد للقراءة فقط.
  • رموز مميزة محددة النطاق. استخدم رمز GitHub دقيق الصلاحيات ومحدوداً بمستودع واحد، أو deploy key عندما تكفي صلاحية القراءة.

يحتوي Claude Code على خيار يتجاوز مطالبات الأذونات بالكامل. على حاسوب محمول أو في مشروع مؤقت، هذا قرارك. أما على خادم يحتفظ بالرموز المميزة، فهو يزيل آخر حاجز بين تعليمات أسيء فهمها وgit push --force. كما أن مطالبات الأذونات التي ستتجاوزها ليست خياراً ثنائياً بالكامل. ومع وصول الوضع التلقائي بوصفه الإعداد الافتراضي الجديد، من المفيد معرفة وضع الأذونات الذي ينبغي تثبيته على خادم لا تراقبه. يشرح تشغيل Claude Code بأمان على خادم ما الذي يغيّره هذا الخيار فعلياً، وكيف تحصر وكيلًا يعمل به، بدءاً من sandbox المضمّن ووصولاً إلى VPS مؤقت.

Deploy key مقابل إعادة توجيه SSH agent

قد تميل إلى ssh -A لكي يتمكن git من استخدام المفتاح الموجود على حاسوبك المحمول. افهم ما يتيحه ذلك: تكشف إعادة توجيه agent مقبس SSH agent المحلي للعمليات التي تعمل باسم ذلك المستخدم على الخادم. يمكن لأي عملية تعمل باسم agent، بما فيها الوكيل، أن تطلب من مفتاحك توقيعاً لأي مضيف يمكنها الوصول إليه، ما دمت متصلاً. وهذا يتجاوز كثيراً عبارة «دع git يسحب هذا المستودع الواحد».

بدلاً من ذلك، أنشئ مفتاحاً على الخادم، وسجّله بوصفه deploy key خاصاً بمستودع واحد، وامنحه صلاحية الكتابة فقط إذا كان الوكيل يحتاج إلى الدفع، ثم اضبط هوية git بحيث يمكن تمييز الالتزامات التي أُنشئت من الخادم:

ssh-keygen -t ed25519 -C "agent deploy key" -f ~/.ssh/id_ed25519_repo
cat ~/.ssh/id_ed25519_repo.pub   # paste into the repo's Deploy Keys
git config --global user.name "Agent (build box)"
git config --global user.email "agent@example.com"

سير العمل باستخدام tmux

ثبّته (`sudo apt install tmux)، ثم أضف إعداداً بسيطاً (~/.tmux.conf`):

set -g mouse on
set -g history-limit 50000
set -g default-terminal "tmux-256color"

تكفي أربعة أوامر للاستخدام اليومي:

tmux new -A -s claude     # attach to session "claude", creating it if absent
# ...run `claude` inside it, work normally...
# Ctrl-b then d           -> detach; everything keeps running
tmux ls                   # list sessions
tmux attach -t claude     # reattach, from this machine or any other
tmux kill-session -t claude

`tmux new -A -s claude هو الأمر الذي يجب حفظه. فهو يعيد الاتصال بالجلسة إذا كانت موجودة، وينشئها إذا لم تكن موجودة. لذلك يغطي الأمر نفسه بدء يوم العمل واستئنافه بعد انقطاع الاتصال. أنشئ له اسماً مستعاراً. داخل الجلسة، يفتح Ctrl-b c نافذة، ويتنقل الأمران Ctrl-b n وCtrl-b p بين النوافذ، بينما يدخل Ctrl-b [ إلى وضع النسخ للتمرير إلى السجلات السابقة (q` يخرج منه).

هناك أمر مهم يجب معرفته بشأن الجلسات التي لا تنهيها أبداً: يعيد الوكيل إرسال المحادثة كاملة في كل دور. لذلك اقرأ ما الذي تنفق عليه جلسة Claude Code طويلة الأمد رموزها قبل أن تترك جلسة قيد التشغيل لمدة أسبوع.

أوضاع الفشل

"اختفت جلستي." يطبع tmux ls القيمة no server running on /tmp/tmux-1000/default. يعني هذا غالباً أن العملية لم تكن تعمل داخل tmux، وأنك اتصلت عبر SSH ثم شغّلت claude مباشرة، فأدى انقطاع الاتصال إلى إنهائها. لا يوجد شيء يمكن استعادته. العادة التي تمنع ذلك هي أن يكون tmux new -A -s <project> أول أمر بعد كل تسجيل دخول.

تصبح النافذة جزءاً صغيراً جداً. يضبط tmux حجم الجلسة وفقاً لأصغر عميل متصل بها، لذلك قد يؤدي عميل قديم ما زال متصلاً من جهاز آخر إلى ضغط العرض. افصل العملاء الآخرين قسراً عند الاتصال باستخدام: tmux attach -d -t claude.

يطبع البناء Killed. تظهر كلمة واحدة بلا stack trace. تحقّق باستخدام sudo dmesg -T | grep -i -E 'out of memory|killed process'؛ فقد اختار kernel OOM killer العملية الأكبر. وقد ترى من Node بدلاً من ذلك FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory. الإصلاحات بالترتيب: أضف swap كما سبق، وقلّل التوازي في الاختبارات والمصرّف، وارفع حجم heap في Node باستخدام NODE_OPTIONS=--max-old-space-size=...، أو زد حجم VPS. قد يختار OOM killer أيضاً خادم tmux بدلاً من البناء، فتنتهي جلستك معه. وإذا كان systemd-oomd قيد التشغيل، فقد ينهي شريحة مستخدم كاملة بالنتيجة نفسها.

npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. هذا يعني تثبيتاً عاماً داخل prefix مملوك لـ root. استخدم prefix ~/.npm-global المذكور أعلاه. إذا شغّلت sudo npm في وقت سابق، فقد ترى أيضاً Your cache folder contains root-owned files. أصلح ذلك باستخدام sudo chown -R $(id -u):$(id -g) ~/.npm.

claude: command not found، لكن أحياناً فقط. يوجد export الخاص بـ PATH داخل ~/.bashrc أسفل الحارس "If not running interactively, don't do anything"، لذلك تتجاوزه الصدَفات غير التفاعلية. انقل export إلى أعلى ذلك الحارس، وأبقِه في ~/.bashrc، وليس في ~/.profile. قد يبدأ tmux صدَفات غير متصلة بتسجيل الدخول، وهذه الصدَفات تقرأ ~/.bashrc ولا تصل أبداً إلى ~/.profile.

ألوان مشوّهة بعد الاتصال. السبب هو عدم تطابق TERM. ويعالج ذلك السطر default-terminal المذكور أعلاه.

تختفي الجلسات بعد إعادة التشغيل. هذا ليس خطأً؛ خادم tmux عملية، وإعادة التشغيل تنهيه. تحقّق من uptime.

ما الذي يتعطل مع نمو هذا الإعداد

مشاريع أكثر. استخدم جلسة tmux واحدة لكل مستودع، وسمِّها باسم المستودع؛ تصبح tmux ls لوحة التحكم لديك. إذا أهملت تسمية الجلسات، فستحصل على جلسات 0 و1 و2. عند تشغيل عدة جلسات في الوقت نفسه، لا يلزم أن تعمل كل واحدة بمعزل عن الأخرى، إذ يمكن لـجلسة واحدة تمرير رسالة إلى جلسة أخرى على الخادم نفسه، وهذا مفيد عندما يريد agent ينفذ عملية refactor طويلة أن يطلب من agent آخر تشغيل الاختبارات. تتوسع المنافذ بالطريقة نفسها. إذا احتاجت 6 مستودعات جميعاً إلى :3000، فقد حان وقت التوقف عن تعيين المنافذ يدوياً وترك Reverse Proxy من Traefik يوجّه عدة تطبيقات ضمن Docker Compose بحسب اسم المضيف.

أشخاص أكثر. تكون tmux sockets خاصة بكل مستخدم، لذلك يحصل مطوران على الخادم نفسه على tmux server مستقل لكل منهما، ولا يستطيع أحدهما رؤية جلسات الآخر. تؤدي مشاركة جلسة عبر socket مشترك إلى كتابة الجميع في shell نفسه باستخدام Unix user نفسه، وما يترتب على ذلك من آثار على التدقيق والصلاحيات. استخدام مستخدمين منفصلين هو الخيار البسيط والصحيح.

عمل دون مراقبة. صُممت tmux للجلسات التفاعلية التي تتصل بها. أما المهام المجدولة التي لا يراقبها أحد، فمكانها systemd unit وtimer، حيث تحصل تلقائياً على السجلات وسياسة لإعادة التشغيل والاستمرار بعد الإقلاع. إن استخدام tmux لتشغيل مهمة تشبه مهام cron علامة على أن المهمة تريد أن تصبح service.

ملاحظة أخيرة: اربط dev servers التي يبدأها agent بالعنوان 127.0.0.1، وليس 0.0.0.0، واتصل بها عبر نفق SSH (ssh -L 3000:127.0.0.1:3000 agent@your-server) بدلاً من فتح منافذ في ufw. عندما تبدأ في تمرير 6 منافذ أو أكثر، أو عندما يريد هاتف وحاسوب محمول الوصول إلى المعاينة نفسها، ضع VPN ذاتية الاستضافة من WireGuard على VPS أمامها بدلاً من ذلك: ترتبط dev servers بواجهة خاصة، ويواصل ufw رفض كل الاتصالات الواردة من الواجهة العامة. لا يفيد جدار الحماية إلا إذا توقفت عن فتح الثغرات فيه.

لا يُعد Claude Code الخيار الوحيد: تشغيل coding AI agent على VPS يتيح لك أيضاً تقييم Aider وGoose.

FAQ

هل يواصل Claude Code العمل بعد انقطاع اتصال SSH؟

فقط إذا شغّلته داخل tmux. تكون العملية التي تبدأها مباشرة من صدفة SSH عمليةً فرعية لهذه الصدفة، وتنتهي مع pty عند انقطاع الاتصال. داخل tmux، تنتمي الصدفة إلى خادم tmux المنفصل، لذلك يواصل الوكيل العمل أثناء المهمة، ويعيدك tmux attach إلى سجل التمرير نفسه. اجعل tmux new -A -s <project> الأمر الأول بعد كل تسجيل دخول، وتنتهي المشكلة.

هل ينبغي أن أثبّت CLI باستخدام sudo npm install -g؟

لا. يمنحك global prefix المملوك لـ root أخطاء EACCES عند عمليات التثبيت اللاحقة، كما ينشئ ملفات مملوكة لـ root في npm cache. اضبط prefix في npm على ~/.npm-global (أو استخدم مدير إصدارات مثل nvm)، وثبّت الحزمة باستخدام المستخدم غير المميّز agent، وصدّر ~/.npm-global/bin إلى PATH من ~/.bashrc، قبل الحارس التفاعلي. إذا شغّلت sudo npm من قبل، فأصلح cache باستخدام sudo chown -R $(id -u):$(id -g) ~/.npm.

هل ssh -A آمن لتمرير الوكيل على خادم يشغّل وكيلاً؟

إنه يمنح صلاحيات تتجاوز ما تحتاج إليه المهمة بكثير. يعرّض تمرير الوكيل socket الخاص بـ SSH agent المحلي لكل عملية تعمل باستخدام ذلك المستخدم، لذلك يمكن لأي شيء على الخادم أن يطلب من مفتاحك توقيع طلب لأي مضيف يمكنه الوصول إليه، طوال مدة بقائك متصلاً. أنشئ مفتاح ed25519 على الخادم وسجّله باعتباره deploy key خاصاً بالمستودع، ولا تمنحه صلاحية الكتابة إلا إذا كان الوكيل يحتاج فعلاً إلى الدفع.

لماذا يطبع build لديّ Killed فقط؟

كلمة واحدة من دون stack trace تعني أن kernel OOM killer أنهى العملية. أكّد ذلك باستخدام sudo dmesg -T | grep -i -E 'out of memory|killed process'؛ وقد ترى JavaScript heap out of memory بدلاً من ذلك عند التشغيل من Node. نفّذ الإصلاحات بالترتيب: أضف swapfile، وقلّل التوازي في الاختبارات وcompiler، وارفع NODE_OPTIONS=--max-old-space-size=...، ثم رقِّ موارد VPS. انتبه إلى أن OOM killer قد يختار خادم tmux بدلاً من build، فيُنهي جلستك بالكامل.

tmux أم خدمة systemd؟

يناسب tmux الجلسات التفاعلية التي تتصل بها وتراقبها وتكتب فيها، وهذا يطابق جلسة الوكيل تماماً. أما العمل المجدول الذي لا يراقبه أحد، فينتمي إلى systemd unit وtimer، حيث تتوفر السجلات وسياسة إعادة التشغيل والاستمرار بعد الإقلاع تلقائياً. إذا كنت تستخدم tmux لتشغيل مهمة تشبه مهام cron، فهذه المهمة تحتاج إلى أن تكون خدمة.