اگر برنامه شما هنگام اجرای دستی در SSH درست کار میکند اما همان برنامه وقتی با systemd اجرا میشود متغیرهایی مثل APP_ENV، NODE_ENV، PORT، DATABASE_URL یا مسیرهای سفارشی را پیدا نمیکند، احتمالاً Environment Variableها برای خود سرویس تعریف نشدهاند.
پاسخ سریع: برای چند مقدار ساده از Environment= در بخش [Service] استفاده کنید. اگر تعداد متغیرها زیاد است، یک فایل جداگانه بسازید و با EnvironmentFile= به systemd معرفی کنید. بعد از هر تغییر، systemctl daemon-reload و سپس Restart سرویس را اجرا کنید.
چرا systemd متغیرهای Shell را نمیبیند؟
وقتی در SSH دستور زیر را اجرا میکنید:
export APP_ENV=production
این متغیر در Environment همان Shell و Processهای فرزند آن وجود دارد.
اما یک system service معمولاً توسط System Manager اجرا میشود و Environment تعاملی SSH یا فایلهایی مثل ~/.bashrc و ~/.profile را به همان شکل Shell شما دریافت نمیکند.
⚠️ اضافهکردن یک متغیر به .bashrc و سپس انتظار داشتن از سرویس systemd برای خواندن آن، معمولاً راهحل صحیح نیست.
روش اول: استفاده از Environment= داخل Unit File
برای چند متغیر ساده میتوانید آنها را مستقیم در بخش [Service] قرار دهید.
مثال:
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
Environment=APP_ENV=production
Environment=NODE_ENV=production
Environment=PORT=3000
ExecStart=/opt/myapp/app
Restart=on-failure
[Install]
WantedBy=multi-user.target
بعد از ذخیره فایل:
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
مقدار دارای Space را چطور تعریف کنیم؟
اگر مقدار Environment Variable شامل فاصله باشد، کل Assignment را داخل کوتیشن قرار دهید:
Environment="APP_NAME=Iranica Server Bot"
Environment="LOG_LEVEL=debug mode"
در Environment= نباید فرض کنید Syntax دقیقاً مانند Bash عمل میکند. systemd قواعد Parsing خودش را دارد.
روش دوم: استفاده از EnvironmentFile=
وقتی تعداد متغیرها زیاد میشود، گذاشتن همه آنها داخل Unit File خوانایی را کم میکند. در این حالت استفاده از فایل Environment مناسبتر است.
مثلاً پوشه تنظیمات را بسازید:
sudo mkdir -p /etc/myapp
فایل Environment:
sudo nano /etc/myapp/myapp.env
نمونه محتوا:
APP_ENV=production
NODE_ENV=production
PORT=3000
DB_HOST=127.0.0.1
DB_PORT=5432
حالا در Unit File:
[Service]
User=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/opt/myapp/app
Restart=on-failure
Permission فایل Environment را محدود کنید
اگر فایل Environment شامل اطلاعات حساس است، دسترسی آن را محدود کنید:
sudo chown root:root /etc/myapp/myapp.env
sudo chmod 600 /etc/myapp/myapp.env
⚠️ برای Secretهای حساس مثل Password، Private Key یا Tokenهای مهم، Environment Variable همیشه امنترین Secret Store نیست. در محیطهای حساس بهتر است قابلیت Credentials خود systemd یا یک Secrets Manager مناسب را بررسی کنید.
EnvironmentFile اختیاری با علامت منفی
اگر وجود فایل Environment اجباری نباشد، میتوانید قبل از مسیر از – استفاده کنید:
EnvironmentFile=-/etc/myapp/myapp.env
در این حالت اگر فایل وجود نداشته باشد، systemd صرفاً بهدلیل نبودن آن سرویس را Fail نمیکند.
Environment= و EnvironmentFile= با هم
میتوانید هر دو روش را همزمان استفاده کنید:
[Service]
Environment=APP_ENV=production
EnvironmentFile=/etc/myapp/myapp.env
اگر یک Variable چند بار تعریف شده باشد، ترتیب و منبع تعریف اهمیت دارد. مقادیر فایل Environment میتوانند مقادیر قبلی تعریفشده با Environment= را Override کنند.
روش بهتر برای تغییر سرویسهای Package شده: systemctl edit
اگر سرویس متعلق به Packageهایی مثل Nginx، Redis یا سرویس نصبشده توسط Package Manager است، Unit File اصلی را مستقیماً ویرایش نکنید.
از Drop-in استفاده کنید:
sudo systemctl edit myapp.service
سپس:
[Service]
Environment="APP_ENV=production"
EnvironmentFile=/etc/myapp/myapp.env
این روش باعث میشود تغییر شما با Update شدن Package راحتتر حفظ شود.
API، Bot یا سرویس دائمی روی VPS اجرا میکنید؟
برای سرویسهایی که با systemd اجرا میشوند، تفکیک صحیح Config، Environment و Secretها باعث میشود Deploy، Restart و عیبیابی سرور سادهتر شود. اگر سرویس شما به منابع بیشتری نیاز دارد، میتوانید پلنهای VPS ایرانیکاسرور را بررسی کنید.
تماس با پشتیبانی ایرانیکاسرور:
021-91302467
بعد از تغییر Environment چه دستورهایی اجرا کنیم؟
اگر Unit File یا Drop-in تغییر کرده است:
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
sudo systemctl status myapp.service --no-pager
اگر فقط محتوای فایل EnvironmentFile= را عوض کردهاید، برای اینکه Process جدید مقادیر تازه را دریافت کند باید سرویس Restart شود.
چطور Environment تعریفشده Unit را ببینیم؟
برای متغیرهایی که مستقیم در Unit تعریف شدهاند:
systemctl show myapp.service -p Environment
برای دیدن مسیر Environment Fileها:
systemctl show myapp.service -p EnvironmentFiles
همچنین برای دیدن Unit و Drop-inهای نهایی:
systemctl cat myapp.service
چطور Environment واقعی Process را ببینیم؟
اگر میخواهید مطمئن شوید خود Process مقدار را دریافت کرده است، PID اصلی سرویس را پیدا کنید:
systemctl show myapp.service -p MainPID
سپس برای یک سرویس غیرحساس میتوانید Environment Process را بررسی کنید:
sudo tr '\0' '\n' < /proc/PID/environ
⚠️ این خروجی ممکن است Secret، Password یا Token داشته باشد. آن را در Ticket، Screenshot، Log عمومی یا انجمنها منتشر نکنید.
تفاوت EnvironmentFile با فایل .env برنامهها
فایل .env در بسیاری از Frameworkها مثل Laravel یا Node.js توسط خود Application یا Library مربوطه خوانده میشود.
اما EnvironmentFile= مستقیماً توسط systemd خوانده میشود و Variableها قبل از اجرای Process وارد Environment آن میشوند.
این دو مفهوم شبیه هستند اما دقیقاً یک چیز نیستند. Syntax فایل `.env` بعضی Frameworkها ممکن است قابلیتهایی داشته باشد که systemd EnvironmentFile همان رفتار را ندارد.
آیا میتوان همان فایل .env پروژه را مستقیماً استفاده کرد؟
اگر Syntax فایل با فرمت قابل قبول systemd سازگار باشد، از نظر فنی ممکن است:
EnvironmentFile=/opt/myapp/.env
اما بهتر است ابتدا محتوا، Permission و Syntax آن را بررسی کنید. همچنین اگر Application خودش `.env` را میخواند، ممکن است واردکردن دوباره همان متغیرها در Environment لازم نباشد.
استفاده از Variable در ExecStart
systemd از Environment Variable در Argumentهای ExecStart پشتیبانی میکند.
مثال:
[Service]
Environment=PORT=3000
ExecStart=/opt/myapp/app --port ${PORT}
اما نباید فرض کنید ExecStart= یک خط Bash است.
برای مثال چنین Syntaxهایی بهصورت Shell خودکار تفسیر نمیشوند:
ExecStart=/opt/myapp/app > /var/log/myapp.log
ExecStart=echo $HOME && /opt/myapp/app
Redirection، Pipe، && و بسیاری از قابلیتهای Shell فقط در صورتی کار میکنند که صراحتاً Shell اجرا کنید؛ ولی برای سرویسهای systemd بهتر است تا جای ممکن ExecStart مستقیم و ساده باشد.
مشکل PATH در systemd
یکی از مشکلات رایج این است که دستور در SSH پیدا میشود ولی در systemd نه.
مثلاً:
which node
which python3
which php
بهتر است در Unit File مسیر کامل Binary را قرار دهید:
ExecStart=/usr/bin/node /opt/myapp/server.js
نمونه کامل برای Node.js
[Unit]
Description=Node.js Application
After=network.target
[Service]
Type=simple
User=nodeapp
WorkingDirectory=/opt/nodeapp
Environment=NODE_ENV=production
EnvironmentFile=/etc/nodeapp/nodeapp.env
ExecStart=/usr/bin/node /opt/nodeapp/server.js
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
نمونه کامل برای Python
[Unit]
Description=Python Application
After=network.target
[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
Environment=PYTHONUNBUFFERED=1
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
اگر متغیر اعمال نمیشود چه کنیم؟
این ترتیب را بررسی کنید:
۱. مسیر EnvironmentFile درست باشد.
۲. Permission فایل درست باشد.
۳. Syntax فایل معتبر باشد.
۴. systemctl daemon-reload اجرا شده باشد.
۵. سرویس Restart شده باشد.
۶. Unit و Drop-in نهایی را با systemctl cat بررسی کنید.
۷. Log سرویس را بررسی کنید.
Log:
sudo journalctl -u myapp.service -b -n 100 --no-pager
پرسشهای متداول
چرا export در SSH روی systemd Service اعمال نمیشود؟
چون system service معمولاً Environment همان Shell تعاملی شما را به ارث نمیبرد. متغیر باید برای خود سرویس یا Service Manager تعریف شود.
Environment بهتر است یا EnvironmentFile؟
برای چند مقدار ساده Environment مناسب است. برای تعداد زیاد متغیر یا جداکردن Config از Unit File معمولاً EnvironmentFile خواناتر است.
بعد از تغییر EnvironmentFile باید daemon-reload اجرا کنیم؟
اگر فقط محتوای همان فایل Environment تغییر کرده باشد، معمولاً Reload تعریف Unit لازم نیست؛ اما برای دریافت مقدار جدید باید Process سرویس Restart شود. اگر Unit یا مسیر EnvironmentFile تغییر کرده باشد، daemon-reload نیز اجرا کنید.
آیا Password را در EnvironmentFile قرار دهیم؟
برای برنامههای ساده ممکن است استفاده شود، اما Environment Variable محل ایدهآل برای Secretهای حساس نیست. Permission فایل را محدود کنید و برای محیط حساس قابلیت Credentials یا Secrets Manager را بررسی کنید.
آیا systemd فایل .bashrc را میخواند؟
System serviceها را نباید وابسته به .bashrc یا .profile کاربر طراحی کرد. Environment سرویس را بهصورت صریح در systemd تعریف کنید.
چطور متغیرهای سرویس را ببینیم؟
برای تنظیمات Unit میتوانید از systemctl show SERVICE -p Environment و برای Process واقعی از /proc/PID/environ استفاده کنید؛ ولی مراقب افشای Secretها باشید.
منابع
🔹 systemd.exec؛ Environment و EnvironmentFile
🔹 systemd.service؛ استفاده از Environment Variable در ExecStart
🔹 systemctl؛ مدیریت Environment سرویسها
جمعبندی
برای تنظیم Environment Variable در systemd، روی export داخل SSH یا فایلهای Shell حساب نکنید. برای چند مقدار ساده از Environment= و برای مجموعه بزرگتر از EnvironmentFile= استفاده کنید. بعد از تغییر Unit، daemon-reload و Restart را انجام دهید و مقدار نهایی را با systemctl show یا Environment Process بررسی کنید. اطلاعات حساس را نیز با Permission مناسب و در محیطهای جدی با روشهای امنتر مدیریت کنید.
Environment سرویس هنوز اعمال نمیشود؟
اگر برنامه در SSH درست اجرا میشود اما در systemd Environment متفاوتی دارد، میتوانید برای بررسی Unit File، EnvironmentFile، Permission و Logهای سرویس درخواست فنی ثبت کنید.
تماس با پشتیبانی ایرانیکاسرور:
021-91302467






دیدگاهها (۰)