SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-27

اجرای Claude Code روی VPS با استفاده از tmux

برای جلوگیری از قطع شدن Claude Code هنگام بستن لپ‌تاپ، آن را روی یک VPS لینوکسی و درون tmux اجرا کنید. این راهنما نحوه نصب، تنظیمات امنیتی و مدیریت نشست‌ها را بررسی می‌کند.

مشکل از درب لپ‌تاپ است، نه CLI

اجرای Claude Code روی لپ‌تاپ شما تا زمانی که درب آن را نبندید بدون مشکل پیش می‌رود: با بستن درب، نشست SSH قطع می‌شود، سیگنال SIGHUP به شل ارسال می‌گردد و ایجنت که سه دقیقه از اجرای تست آن گذشته، همراه با نشست متوقف می‌شود. CLI را روی ماشینی اجرا کنید که هرگز به حالت خواب (sleep) نمی‌رود و این کار را درون یک ترمینال مالتی‌پلکسر انجام دهید که پردازش‌هایش فرزند نشست SSH شما نباشند. تمام ترفند همین است؛ در واقع tmux، و نه نحوه نصب، بخش اصلی و حیاتی این راهکار است.

این صفحه درباره نحوه مدیریت سیستمی است که ایجنت‌ها را روی آن در حال اجرا می‌گذارید. اگر سرور لینوکسی ندارید که بتوانید آن را روشن بگذارید، هیچ‌کدام از این موارد برای شما کاربرد ندارد. این تنها پیش‌نیاز صادقانه این راهنماست.

عملکرد واقعی tmux

هنگامی که از طریق SSH متصل می‌شوید، sshd یک shell ایجاد می‌کند (fork) و یک pseudo-terminal به آن اختصاص می‌دهد؛ هر چیزی که از آن shell اجرا کنید، فرزند آن محسوب می‌شود. اگر اتصال قطع شود، kernel آن pty را از بین می‌برد، shell سیگنال SIGHUP را دریافت می‌کند و به نوبهٔ خود، پروسه‌های فرزندش را می‌بندد. در نتیجه، پروسه‌های طولانی‌مدتی که در foreground اجرا می‌شوند، متوقف می‌گردند.

tmux این مالکیت را معکوس می‌کند. دستوری که شما با عنوان tmux تایپ می‌کنید، در واقع یک کلاینت سبک است که از طریق یک unix socket با یک tmux server در ارتباط است؛ سروری که مستقل از ترمینال شما اجرا می‌شود. shellهای داخل یک session، فرزندان آن سرور هستند، نه فرزندان sshd. اگر اتصال SSH را قطع کنید، کلاینت از بین می‌رود اما سرور، session و پروسه‌ای که در حال اجراست، به کار خود ادامه می‌دهند. با اتصال مجدد و اجرای tmux attach، شما به همان shell قبلی با همان تاریخچهٔ اسکرول بازمی‌گردید. nohup نیز در برابر قطع اتصال مقاوم است، اما راهی برای بازگشت به آن ارائه نمی‌دهد؛ شما نمی‌توانید به یک TUI که در پس‌زمینه رها شده است، دوباره متصل شوید. Claude Code تعاملی است؛ بنابراین tmux (یا screen) ابزار مناسب برای این کار است.

تخمین ابعاد سرور

رابط خط فرمان (CLI) یک پردازش Node است؛ این ابزار نیست که منابع سرور را اشغال می‌کند. آنچه منابع را مصرف می‌کند، هر چیزی است که agent از طرف شما اجرا می‌کند: یک build، یک مجموعه تست کامل، tsc، یک language server، یا یک دیتابیس در Docker. ابعاد سرور را بر اساس toolchain انتخاب کنید، نه CLI. حتی اگر قصد استفاده از آن را ندارید، swap اضافه کنید؛ این کار باعث می‌شود در صورت کمبود حافظه (OOM)، به جای کشته شدن ناگهانی پردازش (OOM kill)، با یک 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

دیسک را نیز زیر نظر داشته باشید: مخازن کد، node_modules و ایمیج‌های Docker به‌سرعت فضا را پر می‌کنند. اگر toolchain فراتر از کانتینرها عمل کرده و به ماشین‌های مجازی کامل، یک KVM guest یا یک نود محلی Kubernetes می‌رسد، پیش از نهایی کردن انتخاب، بررسی کنید که پلن انتخابی قابلیت‌های مجازی‌سازی CPU را در اختیار شما قرار می‌دهد یا خیر؛ چرا که اجرای مجازی‌سازی تو در تو روی یک VPS قابلیتی است که باید توسط ارائه‌دهنده برای شما فعال شود و نمی‌توان آن را از داخل guest فعال کرد.

ایجاد کاربر غیر روت (non-root) در ابتدا

یک کاربر اختصاصی با دایرکتوری home مخصوص به خود ایجاد کنید و کلید عمومی (public key) خود را در جای مناسب قرار دهید:

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

پیش از ادامه، ورود به سیستم را از یک ترمینال دوم تست کنید، در حالی که احراز هویت با رمز عبور همچنان به عنوان پشتیبان فعال است؛ اگر با خطای Permission denied (publickey) مواجه شدید، مشکل معمولاً از مالکیت (ownership) یا مجوزهای (mode) دایرکتوری .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

پیکربندی را اعتبارسنجی و بارگذاری مجدد کنید. در حالی که نشست فعلی خود را باز نگه داشته‌اید، یک نشست جدید را از ترمینال دوم تست کنید:

sudo sshd -t && sudo systemctl restart ssh

یک نکته ظریف در Ubuntu 24.04: سرویس sshd به صورت socket-activated است. تنظیمات احراز هویت در 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 را با درک دقیق از مزایای آن نصب کنید: هنگامی که احراز هویت با رمز عبور غیرفعال باشد، حملات brute force به هر حال موفق نخواهند بود؛ این ابزار تنها تلاش‌های ناموفق را از لاگ‌های سیستم (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 فعال باشد، به‌روزرسانی هسته (kernel) باعث راه‌اندازی مجدد سرور شده و تمام نشست‌ها را می‌بندد. آن را غیرفعال بگذارید و در زمان مناسب که پردازش مهمی در جریان نیست، سرور را شخصاً راه‌اندازی مجدد کنید. همین احتیاط برای ارتقای نسخه سیستم‌عامل نیز صادق است: انتقال سرور از Ubuntu 24.04 به 26.04 باعث راه‌اندازی مجدد sshd و هسته می‌شود، بنابراین این کار را در بازه‌ای انجام دهید که هیچ نشست tmux حاوی کارهای مهم نباشد.

نصب Node.js و Claude Code روی Ubuntu

Claude Code یک ابزار CLI مبتنی بر Node است، بنابراین به نسخه به‌روزی از 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 استفاده نکنید. نصب در سطح سیستم با مالکیت root منجر به خطاهای دسترسی در آینده و باقی ماندن فایل‌های متعلق به root در کش npm می‌شود. ابتدا prefix مربوط به npm را به دایرکتوری home کاربر هدایت کنید:

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

دستور export باید در ~/.bashrc قرار گیرد، نه ~/.profile، و باید بالاتر از شرط "If not running interactively, don't do anything" در ابتدای فایل باشد: tmux ممکن است shellهای غیر-login را اجرا کند که ~/.bashrc را می‌خوانند و از ~/.profile عبور می‌کنند، در حالی که ~/.profile فقط برای shellهای login اجرا می‌شود. استفاده از یک Node اختصاصی برای هر کاربر از طریق مدیریت نسخه‌ای مانند nvm نیز به همین نتیجه می‌رسد؛ هدف در هر دو حالت این است که npm install -g هرگز به sudo نیاز نداشته باشد. npm همچنان به‌خوبی کار می‌کند، یا می‌توانید از اسکریپت نصب بومی Anthropic که روش پیش‌فرض مستندشده فعلی است استفاده کنید. پیش از کپی کردن دستورات، مستندات نصب Anthropic را بررسی کنید، زیرا روش‌های نصب تغییر می‌کنند.

برای شروع، دستور claude را داخل یک مخزن (repo) اجرا کنید. اجرای اول شما را در مراحل احراز هویت راهنمایی می‌کند؛ یک سرور بدون رابط گرافیکی (headless) مرورگر ندارد، بنابراین این فرآیند یک URL برای باز کردن در سیستم شخصی‌تان و یک کد برای وارد کردن در ترمینال به شما می‌دهد. (روش دیگر، استفاده از یک API key در متغیرهای محیطی است.) در هر صورت، آن اعتبارنامه اکنون روی سرور ذخیره می‌شود، که ما را به بخشی می‌رساند که افراد معمولاً از آن غافل می‌شوند.

بحث شعاع انفجار

عاملی که دسترسی shell دارد، در واقع یک shell است. این عامل می‌تواند هر چیزی را که کاربرِ اجراکنندهٔ آن دسترسی خواندنش را دارد، بخواند و در هر مسیری که آن کاربر اجازهٔ نوشتن دارد، تغییر ایجاد کند. این یک نقد به ابزار نیست، بلکه تعریف ماهیت آن است؛ به همین دلیل است که حساب کاربریِ تحتِ اجرای آن، از هر تنظیمات فردی دیگری اهمیت بیشتری دارد.

  • کاربر اختصاصی و بدون امتیاز (unprivileged). بدون عضویت در گروه sudo و بدون دایرکتوری home مشترک با حساب کاربری شخصی شما.
  • عدم وجود اعتبارنامه‌های production روی سیستم. هیچ فایل ~/.aws/credentials حاوی کلیدهای prod، هیچ فایل .env کپی‌شده از محیط production، و هیچ رمز عبور دیتابیسی که دسترسی نوشتن به منابع حساس داشته باشد، نباید وجود داشته باشد. به عامل، فقط اعتبارنامهٔ محیط staging یا دسترسی فقط‌خواندنی (read-only) بدهید.
  • توکن‌های محدودشده (Scoped tokens). یک توکن GitHub با دسترسی دقیق که فقط به یک مخزن محدود شده است؛ یا یک deploy key زمانی که دسترسی خواندن کافی است.

ابزار Claude Code پرچمی دارد که درخواست‌های مجوز (permission prompts) را به‌طور کامل نادیده می‌گیرد. روی لپ‌تاپ و برای یک پروژهٔ موقت، این تصمیم با شماست. اما روی سروری که حاوی توکن‌هاست، این کار آخرین سد دفاعی میان یک دستور اشتباه و یک git push --force را از بین می‌برد. درخواست‌هایی که نادیده می‌گیرید، همگی از نوع «همه یا هیچ» نیستند و با رسیدن حالت خودکار به عنوان پیش‌فرض جدید، لازم است بدانید سروری که تحت نظارت مستقیم شما نیست، باید روی کدام حالت مجوز قفل شود. اینکه این پرچم دقیقاً چه چیزی را تغییر می‌دهد و چگونه می‌توان عاملی که با آن اجرا می‌شود را محدود کرد (از sandbox داخلی گرفته تا یک VPS یک‌بارمصرف)، در بخش اجرای ایمن Claude Code روی سرور توضیح داده شده است.

تفاوت deploy key با SSH agent forwarding

وسوسه‌انگیز است که از ssh -A استفاده کنید تا git بتواند از کلید موجود روی لپ‌تاپ شما استفاده کند. بدانید که این کار چه دسترسی‌هایی می‌دهد: agent forwarding سوکت SSH agent محلی شما را در اختیار پردازش‌هایی قرار می‌دهد که با آن کاربر روی سرور اجرا می‌شوند. هر چیزی که با کاربر agent اجرا شود، از جمله خودِ عامل، می‌تواند تا زمانی که شما متصل هستید، از کلید شما برای احراز هویت در هر میزبانی که به آن دسترسی دارد، استفاده کند. این دسترسی بسیار فراتر از «اجازه دادن به git برای pull کردن یک مخزن خاص» است.

به‌جای این کار، یک کلید روی سرور تولید کنید، آن را به عنوان یک deploy key برای همان مخزن ثبت کنید (فقط در صورتی که عامل نیاز به push دارد، دسترسی نوشتن بدهید) و یک git identity تنظیم کنید تا commitهای ارسالی از سرور قابل شناسایی باشند:

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 دستوری است که باید به خاطر بسپارید؛ اگر session وجود داشته باشد به آن متصل می‌شود و اگر نباشد آن را می‌سازد، بنابراین یک دستور هم برای شروع روز و هم برای بازگشت پس از قطع اتصال کافی است. برای آن یک alias تعریف کنید. داخل یک session، دستور Ctrl-b c یک پنجره باز می‌کند، Ctrl-b n و Ctrl-b p بین آن‌ها جابه‌جا می‌شوند و Ctrl-b [ وارد حالت copy mode می‌شود تا بتوانید به عقب اسکرول کنید (q از این حالت خارج می‌شود).

یک نکته درباره sessionهایی که هرگز نمی‌بندید (kill نمی‌کنید): agent در هر نوبت کل گفتگو را دوباره ارسال می‌کند، بنابراین پیش از آنکه یک session از Claude Code را برای یک هفته باز بگذارید، مواردی که یک session طولانی‌مدت 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' تأیید کنید؛ 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 در حال اجرا باشد، می‌تواند کل slice کاربر را با همان نتیجه از بین ببرد.

npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. یک نصب سراسری (global) در مسیری که متعلق به root است. از پیشوند ~/.npm-global که در بالا ذکر شد استفاده کنید. اگر قبلاً sudo npm را اجرا کرده‌اید، ممکن است Your cache folder contains root-owned files را نیز ببینید؛ با sudo chown -R $(id -u):$(id -g) ~/.npm آن را تعمیر کنید.

claude: command not found، اما فقط گاهی اوقات. خروجی PATH شما در ~/.bashrc زیر محافظ «اگر به صورت تعاملی اجرا نمی‌شود، هیچ کاری نکن» قرار دارد، بنابراین shellهای غیرتعاملی از آن عبور می‌کنند. خروجی را به بالای آن محافظ منتقل کنید و آن را در ~/.bashrc نگه دارید، نه ~/.profile: tmux ممکن است shellهای غیر-login را شروع کند که ~/.bashrc را می‌خوانند و هرگز به ~/.profile دسترسی پیدا نمی‌کنند.

به‌هم‌ریختگی رنگ‌ها پس از اتصال. عدم تطابق TERM؛ خط default-terminal در بالا راه‌حل این مشکل است.

نشست‌ها پس از راه‌اندازی مجدد (reboot) ناپدید می‌شوند. این یک باگ نیست: سرور tmux یک پردازش است و راه‌اندازی مجدد به آن پایان می‌دهد. uptime را بررسی کنید.

چه چیزی با رشد این ساختار دچار اختلال می‌شود

پروژه‌های بیشتر. برای هر مخزن یک session در tmux ایجاد کنید و آن را همنام مخزن بگذارید؛ tmux ls در این حالت داشبورد شما خواهد بود. اگر نظم در نام‌گذاری را رعایت نکنید، با sessionهایی مثل 0، 1 و 2 مواجه می‌شوید. وقتی چندین session همزمان فعال باشند، لزومی ندارد که در انزوا کار کنند، چرا که یک session می‌تواند پیامی را به session دیگری روی همان سیستم ارسال کند؛ این قابلیت زمانی مفید است که agent مسئول یک refactor طولانی، بخواهد agent دیگری را برای اجرای تست‌ها فراخوانی کند. پورت‌ها نیز به همین ترتیب دچار آشفتگی می‌شوند؛ وقتی شش مخزن همگی نیاز به پورت :3000 دارند، زمان آن رسیده که تخصیص دستی را متوقف کنید و اجازه دهید یک reverse proxy مانند Traefik که با Docker Compose کار می‌کند، هدایت درخواست‌ها را بر اساس نام دامنه (hostname) انجام دهد.

افراد بیشتر. سوکت‌های tmux مختص هر کاربر هستند، بنابراین دو توسعه‌دهنده روی یک سیستم، هر کدام سرور tmux خود را دارند و نمی‌توانند sessionهای یکدیگر را ببینند. اشتراک‌گذاری یک session از طریق یک سوکت مشترک به این معناست که همه به عنوان یک کاربر یونیکس واحد در یک shell تایپ می‌کنند که پیامدهای امنیتی و نظارتی خاص خود را دارد. استفاده از کاربران مجزا، راهکار اصولی و صحیح است.

کارهای بدون نظارت. ابزار tmux برای sessionهای تعاملی است که به آن‌ها متصل می‌شوید. کارهایی که طبق زمان‌بندی و بدون نظارت مستقیم اجرا می‌شوند، باید در قالب یک unit و timer در systemd تعریف شوند؛ جایی که لاگ‌گیری، سیاست restart و پایداری پس از reboot به‌صورت خودکار فراهم است. استفاده از tmux برای اجرای کارهای زمان‌بندی‌شده (cron-like)، نشان‌دهنده این است که آن کار باید به یک سرویس تبدیل شود.

یک نکته پایانی: سرورهای توسعه (dev servers) که توسط agent اجرا می‌شوند را روی 127.0.0.1 تنظیم کنید، نه 0.0.0.0، و به جای باز کردن پورت‌ها در ufw، از طریق یک تونل SSH (ssh -L 3000:127.0.0.1:3000 agent@your-server) به آن‌ها دسترسی پیدا کنید. زمانی که تعداد پورت‌های فوروارد شده زیاد شد، یا نیاز داشتید همزمان با گوشی و لپ‌تاپ به یک پیش‌نمایش دسترسی داشته باشید، به‌جای آن یک WireGuard VPN روی VPS خود راه‌اندازی کنید: سرورهای توسعه به یک اینترفیس خصوصی متصل می‌شوند و ufw همچنان دسترسی‌ها از اینترفیس عمومی را مسدود نگه می‌دارد. فایروال تنها در صورتی کارآمد است که از ایجاد حفره‌های امنیتی در آن خودداری کنید.

Claude Code تنها گزینه موجود نیست: اجرای یک AI agent برای کدنویسی روی VPS شامل بررسی گزینه‌هایی مانند Aider و Goose نیز می‌شود.

FAQ

آیا Claude Code پس از قطع اتصال SSH به اجرا ادامه می‌دهد؟

فقط در صورتی که آن را داخل tmux اجرا کرده باشید. فرآیندی که مستقیماً از shell مربوط به SSH شروع شود، فرزند آن shell محسوب شده و با بسته شدن pty در هنگام قطع اتصال، از بین می‌رود. داخل tmux، آن shell متعلق به سرورِ جداشدهٔ (detached) tmux است؛ بنابراین agent در حین انجام وظیفه به کار خود ادامه می‌دهد و tmux attach شما را به همان وضعیت اسکرول قبلی بازمی‌گرداند. کافی است tmux new -A -s <project> را به اولین دستور پس از هر بار ورود تبدیل کنید تا این مشکل برطرف شود.

آیا باید CLI را با sudo npm install -g نصب کنم؟

خیر. استفاده از پیشوند سراسری (global prefix) که متعلق به root باشد، در نصب‌های بعدی باعث بروز خطاهای EACCES و ایجاد فایل‌هایی با مالکیت root در کش npm می‌شود. پیشوند npm را روی ~/.npm-global تنظیم کنید (یا از یک مدیریت‌کننده نسخه مانند nvm استفاده کنید)، آن را به عنوان کاربر بدون امتیاز agent نصب کنید و ~/.npm-global/bin را از فایل ~/.bashrc، پیش از بخش interactive guard، به PATH صادر (export) کنید. اگر قبلاً یک بار sudo npm را اجرا کرده‌اید، کش را با sudo chown -R $(id -u):$(id -g) ~/.npm تعمیر کنید.

آیا استفاده از agent forwarding در ssh -A روی سروری که خودش agent دارد، امن است؟

این کار دسترسی بسیار بیشتری از آنچه برای انجام وظیفه نیاز است، فراهم می‌کند. Forwarding باعث می‌شود socket مربوط به SSH agent محلی شما در معرض تمام فرآیندهایی قرار بگیرد که با آن کاربر اجرا می‌شوند؛ بنابراین هر چیزی روی آن سرور می‌تواند تا زمانی که شما متصل هستید، از کلید شما برای احراز هویت در هر هاستی که به آن دسترسی دارد، استفاده کند. یک کلید ed25519 روی سرور تولید کنید و آن را به عنوان یک deploy key مختص همان مخزن (repository) ثبت کنید؛ و تنها در صورتی که agent واقعاً نیاز به push کردن دارد، دسترسی نوشتن (write access) به آن بدهید.

چرا بیلد من فقط Killed را چاپ می‌کند؟

یک کلمه بدون stack trace نشان‌دهنده عملکرد OOM killer در هسته سیستم‌عامل است. آن را با sudo dmesg -T | grep -i -E 'out of memory|killed process' تأیید کنید؛ در Node ممکن است به جای آن JavaScript heap out of memory را ببینید. اصلاحات را به ترتیب انجام دهید: یک swapfile اضافه کنید، موازی‌سازی (parallelism) تست‌ها و کامپایلر را محدود کنید، مقدار NODE_OPTIONS=--max-old-space-size=... را افزایش دهید و در نهایت ظرفیت VPS را ارتقا دهید. مراقب باشید که OOM killer ممکن است به جای فرآیند بیلد، سرور tmux را انتخاب کند و کل نشست (session) شما را از بین ببرد.

tmux یا یک سرویس systemd؟

tmux برای نشست‌های تعاملی که به آن‌ها متصل می‌شوید، آن‌ها را نظارت می‌کنید و در آن‌ها تایپ می‌کنید مناسب است؛ که دقیقاً همان چیزی است که یک نشست agent به آن نیاز دارد. کارهایی که طبق زمان‌بندی و بدون نظارت اجرا می‌شوند، باید در قالب یک unit و timer در systemd قرار بگیرند، جایی که ثبت لاگ، سیاست راه‌اندازی مجدد و بقا پس از بوت به صورت خودکار فراهم است. اگر برای اجرای یک کار زمان‌بندی‌شده (cron-shaped) به سراغ tmux می‌روید، آن کار باید به یک سرویس تبدیل شود.