پروژه روی لپتاپ کار میکند، تستها هم سبز هستند. اما تا وقتی هیچ آدرسی برای باز کردن نباشد، از نظر کاربر وجود ندارد. راه سنتی، پر کردن همین شکاف با یک سرور دستی است: اوبونتو نصب کنی، nginx را تنظیم کنی، SSL بگیری، systemd بنویسی، و هر بار که کد عوض شد همان مسیر را دوباره بروی.
راه دوم این است که سرور را نبینی. ریپو را وصل میکنی، پورت را میدهی، بقیهٔ کارها را پلتفرم انجام میدهد. اسم این مدل PaaS است و برای بیشتر پروژهها کوتاهترین راه تا یک URL زنده.
در مستندات فارسی، به همین کار میگویند استقرار؛ deploy و «دیپلوی» هم همان است. هر سه را یکجا میبینی، پس بهتر است از اول بدانی سر و کارت با یک چیز است.
این مقاله آموزش عملی است، نه تعریف مفهوم: از اتصال ریپو تا اولین دیپلوی موفق، و بعد همان چیزهایی که بیلد را میخوابانند. اگر هنوز مطمئن نیستی PaaS برای پروژهات درست است یا VPS، اول سرویس PaaS چیست و چه فرقی با VPS دارد؟ را بخوان و بعد برگرد.
در یک نگاه
- پیشنیاز
- ریپو در GitHub، GitLab یا Gitea، با یک
Dockerfileیا یکی از تایپهای آماده - زمان
- حدود ۱۰ دقیقه تا اولین URL زنده
- پورت
- الزامی، بین ۱ تا ۶۰۰۰۰ و همان پورتی که اپ روی آن گوش میدهد
- دامنه
- سابدامین HTTPS رایگان برای هر پروژه، یا دامنهٔ خودت
- هزینه
- ساعتی و بهازای منابع مصرفی، با ۱۰۰ هزار تومان اعتبار اولیه
«بدون سرور» یعنی سرور هست، ولی تو با آن کار نمیکنی
هیچچیز جادویی نیست. یک ماشین واقعی هست و کسی هم باید نگهداریاش کند؛ آن کسی تو نیستی. مسئولیتها اینطور تقسیم میشود:
| کار | با تو | با پلتفرم |
|---|---|---|
| نوشتن کد و وابستگیها | ✅ | — |
Dockerfile و مسیر اجرای اپ | ✅ | — |
| پورت و متغیرهای محیطی | ✅ | — |
| سیستمعامل، کرنل و آپدیت امنیتی | — | ✅ |
| اجرای کانتینر و ریاستارت خودکار | — | ✅ |
| SSL، دامنه و پروکسی | — | ✅ |
| بیلد و دیپلوی روی هر push | — | ✅ |
و صادقانه: این مسیر برای همه نیست. اگر به روت سرور، SSH، کرونجاب سیستمی یا تنظیم فایروال نیاز داری، جایش VPS است. PaaS برای اپی است که یک پروسه بالا میآورد و چند متغیر محیطی میخواهد، نه بیشتر از این.
قبل از دیپلوی: سه چیز را درست کن
اگر این سه مورد مرتب باشد، دیپلوی یکبار انجام میشود و دیگر دست نمیخورد:
- ریپو را روی گیت بگذار: GitHub، GitLab یا Gitea. پلتفرم از ریپو بیلد میگیرد، از پوشهٔ لپتاپ تو نه.
- اجرا را قابل تکرار کن: یا
Dockerfileبده، یا یکی از تایپهای آماده را انتخاب کن. «روی سیستم من کار میکند» اینجا به کار نمیآید؛ باید نوشته باشد چه نسخهای از زبان، چه دستوری برای نصب و چه دستوری برای اجرا. - پورت را از محیط بخوان: اپ باید روی
0.0.0.0گوش بدهد، نه اینکه پورت داخل کد هاردکد شود:const port = Number(process.env.PORT ?? 3000); app.listen(port, "0.0.0.0");
قدم ۱: حساب گیت را وصل کن
در پنل حساب گیت را وصل کن و اجازهٔ دسترسی بده. دسترسی فقط به همان ریپوهایی داده میشود که انتخاب میکنی، نه کل حساب. اگر ریپو در فهرست دیده نشد، مجوز را دوباره بده یا حساب گیت دیگری وصل کن؛ روی سازمان و ریپوی خصوصی این تنها جایی است که معمولاً گیر میکند.
قدم ۲: پروژه را بساز
چهار انتخاب داری و هر چهارتا روی نتیجه اثر میگذارد:
- شاخه را انتخاب کن: همان شاخهای که باید روی پروداکشن باشد، معمولاً
main. - نوع بیلد را بگذار روی Docker: مسیر فایل را
Dockerfileبده، یا مسیر دقیقش اگر داخل زیرپوشه است. تایپهای آماده هم برای فریمورکهای رایجاند و Dockerfile نمیخواهند. - پورت را درست بده: همان پورتی که اپ روی آن گوش میدهد. باید همینجا بدهی؛ اگر اشتباه باشد، بیلد سبز میشود ولی سایت باز نمیشود.
- کلاستر را همان اول درست انتخاب کن: ایران یا آلمان. این دو از هم جدا هستند و پروژهها بینشان سینک نمیشوند، پس برگشتن عقب یعنی ساختن از نو: مخاطب اصلی داخل ایران است، یا پروژه به سرویس تحریمشده (APIهای هوش مصنوعی، درگاه پرداخت خارجی) وصل میشود؟
بهمحض ساخت پروژه، اولین پایپلاین خودکار اجرا میشود و وبهوک روی ریپازیتوری ثبت میشود؛ از این لحظه هر push روی آن شاخه یک دیپلوی تازه میسازد.
قدم ۳: بیلد و استقرار را دنبال کن
از لحظهٔ ساخت تا زنده شدن، وضعیت پروژه را میبینی. اگر گیر کردی، ترتیبش این است:
| وضعیت | معنی | کاری که باید بکنی |
|---|---|---|
checking-balance | پلتفرم موجودی حساب را چک میکند | اگر رد شد، شارژ کن |
creating | کلون، بیلد و آمادهسازی منابع | صبر کن و پایپلاین را بخوان |
insufficient-balance | موجودی کافی نیست | شارژ، بعد دیپلوی مجدد |
running | اپ زنده و قابل دسترس | دامنه را باز کن و تست واقعی بزن |
failed | یک مرحله خطا داده | لاگ همان مرحله را بخوان |
پایپلاین سه مرحله دارد: کلون، بیلد، استقرار. خطا در هرکدام معنی دیگری دارد، پس لاگ همان مرحله را بخوان؛ بیلد مجدد کامل فقط وقت میگیرد و همان خطا را تکرار میکند.
یک Dockerfile که واقعاً کار میکند
این نسخه برای اپ Node کمحجم و قابلاتکاست. چهار نکتهاش را پایین ببین:
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "server.js"]package.jsonرا قبل از کد کپی کن: تا وقتی وابستگیها عوض نشدهاند، نصبشان دوباره اجرا نمیشود. تغییر یک فایل کد نباید کل وابستگیها را از نو نصب کند.npm ciرا بهnpm installترجیح بده: ازpackage-lock.jsonاستفاده میکند و بیلد را قابل تکرار میکند. بدون lockfile، هر بیلد میتواند نسخهٔ دیگری نصب کند..dockerignoreرا فراموش نکن:node_modules،.gitو.envنباید وارد ایمیج شوند.EXPOSEرا جدی نگیر: فقط مستندسازی است و پورت واقعی را پنل تعیین میکند. حذفش چیزی را خراب نمیکند؛ ولی پورت اشتباه، سایت را میخواباند.
برای Python، PHP، Go و بقیه هم منطق یکی است: اول وابستگیها، بعد کد، آخرش یک دستور اجرای مشخص. اگر Dockerfile نداری، قبل از هر چیز محلی docker build -t test . بزن و اجرا کن؛ وقتی آنجا بالا آمد، دیپلوی کن.
متغیرهای محیطی و تلهٔ بیلد-آرگ
رمز دیتابیس و کلید API داخل ریپو نمیآیند؛ جایش متغیرهای محیطی است. این متغیرها به اپ زنده تزریق میشوند، ولی یک نکته هست که خیلیها را زمین میزند:
متغیرهای محیطی به بیلد هم پاس میشوند، ولی فقط اگر Dockerfile آنها را اعلام کرده باشد.بیلد و اجرا دو محیط جدا هستند. متغیری که فقط داخل کانتینر زنده وجود دارد، هنگام npm run build در دسترس نیست. فرانتاندهایی که مقدار را داخل خروجی بیلد میپزند همینجا گیر میکنند (VITE_* در Vite، NEXT_PUBLIC_* در Next.js): تنظیمات درست است، ولی دیر رسیده. راهحل، اعلام صریح است:
ARG VITE_API_URL
ENV VITE_API_URL=$VITE_API_URL
RUN npm run buildیا الگوی کوتاهتر: اگر پلتفرم خط نشانهٔ #INSERT-ARGS را پشتیبانی میکند، آن را قبل از مرحلهٔ بیلد بگذار تا برای هر متغیر یک خط ARG تولید شود. اینطوری برای اضافه کردن هر متغیر جدید لازم نیست Dockerfile را دست بزنی.
پلتفرم برای npm یک میرور پیشفرض دارد و میتوانی برای هر پروژه رجیستری را عوض کنی. مقدار به بیلد در قالب آرگومان NPM_REGISTRY میرسد و Dockerfile سفارشی فقط وقتی آن را میگیرد که ARG NPM_REGISTRY داشته باشد. اگر رجیستری کند شد یا قطع، تغییر میرور npm بدون دست زدن به Dockerfile را ببین.
دامنه و HTTPS
هر پروژه از لحظهٔ ساخت یک سابدامین HTTPS رایگان میگیرد (<id>.lexoyacloud.ir)، پس برای دیدن نتیجه لازم نیست دامنه بخری. برای دامنهٔ خودت، رکوردی که پنل نشان میدهد را در DNS ثبت کن و صبر کن تا پروپاگیشن تمام شود؛ گواهی خودش صادر میشود.
اولین تست واقعی سه چیز است: صفحهٔ اصلی، یک مسیر API که به دیتابیس وصل است، و یک فایل استاتیک. اگر هر سه جواب دادند، دیپلوی تمام است. 502 و 504 روی صفحهٔ اصلی تقریباً همیشه یعنی پورت یا آدرس گوش دادن اشتباه است، نه مشکل کد.
دیتابیس: داخل پروژه یا جدا؟
جواب کوتاه: داخل کانتینر خود اپ نه. فایلسیستم کانتینر با هر دیپلوی عوض میشود و ممکن است داده را با خودش ببرد. راه درست، دیتابیس مدیریتشده است: آدرس داخلی و پورت از تنظیمات سرویس گرفته میشود و اتصال داخل شبکهٔ حساب انجام میشود، بدون اینکه چیزی را عمومی کنی.
- آدرس را از محیط بخوان: نه
localhost. آدرس داخلی معمولاً شکل<id>:5432دارد. - بکاپ شبانه را جدی بگیر: دلیل انتخاب دیتابیس مدیریتشده فقط نصب راحت نیست؛ بکاپ و نگهداری خودکار است.
- آپلودها را جدا نگه دار: فایلهای آپلودی هم مثل دادهاند. اگر روی فایلسیستم کانتینر ذخیره شوند، بعد از اولین دیپلوی میپرند. یا یک volume را به مسیر آپلود وصل کن، یا از همان اول فایلها را در S3 بگذار.
اگر پروژهات به دیتابیس اختصاصی و کلاستر با failover نیاز دارد، آن دیگر بحث PaaS نیست: PostgreSQL HA را ببین.
از این به بعد: push یعنی دیپلوی
وبهوک روی ریپازیتوری نشسته، پس جریان عادیات این میشود: کد را روی شاخهٔ انتخابشده push میکنی، بیلد خودش اجرا میشود، و اگر سبز شد نسخهٔ جدید جای قبلی مینشیند. هیچ دستوری روی سرور نمیزنی و هیچ SSHای لازم نیست.
دو عادت جلوی بیشتر خرابیها را میگیرد. اول، قبل از push روی شاخهٔ اصلی، بیلد را روی یک شاخهٔ دیگر امتحان کن تا پروداکشن هرگز روی بیلد نشکسته نرود. دوم، بعد از هر دیپلوی پایپلاین را یکبار باز کن؛ دیپلوی سبز بدون تست واقعی یعنی هنوز چیزی نمیدانی.
خطاهای رایج و رفعشان
| نشانه | علت معمول | راهحل |
|---|---|---|
| بیلد سبز، صفحه 502 | پورت اشتباه یا گوش دادن روی 127.0.0.1 | پورت پنل را با پورت اپ یکی کن و روی 0.0.0.0 بایست |
| متغیر محیطی در اپ هست، در بیلد نیست | بیلد و اجرا دو محیط جدا هستند | ARG را در Dockerfile اعلام کن یا از #INSERT-ARGS استفاده کن |
خطای 403 هنگام دانلود ایمیج | مشکل گذرا در رجیستری Docker Hub | یکبار دوباره بیلد کن؛ اگر تکرار شد ایمیج پایه را از رجیستری آینهای بردار |
npm install قطع یا ناقص میشود | رجیستری پیشفرض کند یا در دسترس نیست | رجیستری پروژه را عوض کن و بیلد مجدد بزن |
| بیلد شروع نمیشود | وضعیت insufficient-balance | حساب را شارژ کن و دیپلوی را دوباره بزن |
| فایلهای آپلودی بعد از دیپلوی نیستند | نوشتن روی فایلسیستم کانتینر | volume یا S3 را به مسیر آپلود وصل کن |
خطای 403 معمولاً با عوض کردن ایمیج پایه با نسخهٔ آینهای حل میشود: مثلاً nginx:1.24.0 میشود repo.lexoya.com/library/nginx:1.24.0 و ایمیجهای Node از repo.lexoya.com/lexoya/node-base-image:20 گرفته میشوند.
هزینه: کِی PaaS از VPS گرانتر میشود؟
منابع در PaaS ساعتی حساب میشوند و حداقل مصرف ماهانه نداری. اپی با ۲ vCPU، ۴ گیگ رم و ۲۰ گیگ دیسک، در آبان ۱۴۰۵ حدود ۱٬۸۲۰ تومان در ساعت بود، یعنی نزدیک ۱٬۳۱۰٬۴۰۰ تومان برای یک ماه روشن بودن کامل. هر بیلد هم در کلاستر ایران حدود ۲ هزار تومان است.
حساب ساده است: ساعت × ۷۲۰ همان ماهانه است. پس مدل ساعتی وقتی برنده است که اپ تمام ماه روشن نباشد یا منابعش را بالا و پایین ببری. محصولی که ۲۴ ساعته زیر بار است با هزینهٔ ثابت و پیشبینیپذیر بهتر حساب میکند و آنجاست که یک VPS ماهانه جواب میدهد.
هزینهٔ شروع کم است: هنگام ثبتنام ۱۰۰ هزار تومان اعتبار اولیه میگیری و سابدامین و HTTPS هر پروژه رایگان است. عدد دقیق منابع خودت را با صفحهٔ قیمت چک کن.
چکلیست قبل از اولین دیپلوی واقعی
- ریپو و شاخهٔ درست را انتخاب کردهای و وبهوک ثبت شده.
Dockerfileرا محلی build و run کردهای.- اپ روی
0.0.0.0و روی همان پورتی گوش میدهد که در پنل دادهای. - هیچ رمز و کلیدی داخل ریپو نیست و همه در متغیرهای محیطیاند.
- متغیرهایی که باید در بیلد باشند،
ARGدارند. - دیتابیس جداست و آدرسش از محیط خوانده میشود، نه
localhost. - مسیر آپلود به volume یا S3 وصل است، نه فایلسیستم کانتینر.
- یکبار کل مسیر را روی یک شاخهٔ آزمایشی تمرین کردهای.
اولین پروژهات را بدون سرور بالا بیاور
ساخت حساب و ۱۰۰ هزار تومان اعتبار پلتفرم PaaS لکسویا مستندات PaaS
سؤالات متداول
PaaS چیست و چه فرقی با VPS دارد؟
در PaaS فقط کد، Dockerfile و متغیرهای محیطی دست توست؛ سیستمعامل، اجرای کانتینر، SSL، دامنه و بیلد خودکار کار پلتفرم است. در VPS روت سرور در اختیار خودت است و همان کارها را باید خودت انجام بدهی. اگر به SSH، کرونجاب سیستمی یا نصب یک سرویس خاص نیاز داری، VPS بگیر؛ اگر اپت یک پروسه است با چند متغیر محیطی، PaaS سریعتر و کمدردسرتر است.
برای دیپلوی روی PaaS حتماً Dockerfile لازم است؟
نه. تایپهای آماده برای فریمورکهای رایج (مثل Next.js، Node و FastAPI) خودشان بیلد و اجرا را میدانند و Dockerfile نمیخواهند. برای پروژهٔ خودت یا هر چیز غیرمتعارف، نوع بیلد Docker با مسیر Dockerfile مطمئنترین راه است، چون همان چیزی اجرا میشود که روی سیستم خودت هم اجرا شده.
دیپلوی سبز شده ولی سایت 502 میدهد؛ مشکل کجاست؟
تقریباً همیشه پورت یا آدرس گوش دادن. یا پورت ثبتشده در پنل با پورتی که اپ روی آن گوش میدهد یکی نیست، یا اپ روی 127.0.0.1 گوش میدهد و از بیرون کانتینر قابل دسترس نیست. پورت باید از متغیر محیطی خوانده شود و listen روی 0.0.0.0 باشد.
آیا هر push خودکار دیپلوی میشود؟
بله. هنگام ساخت پروژه یک webhook روی ریپازیتوری ثبت میشود، پس هر push روی شاخهای که انتخاب کردهای یک بیلد و دیپلوی تازه میسازد. اگر نمیخواهی پروداکشن روی بیلد نشکسته برود، تغییرها را اول روی یک شاخهٔ دیگر push کن و بعد merge کن.
متغیرهای محیطی به مرحلهٔ بیلد هم میرسند؟
به بیلد هم پاس میشوند، اما فقط اگر Dockerfile آنها را با ARG اعلام کرده باشد؛ چون بیلد و اجرا دو محیط جدا هستند. این برای مقادیری که هنگام بیلد داخل خروجی میپزند حیاتی است، مثل VITE_* در Vite و NEXT_PUBLIC_* در Next.js. با گذاشتن ARG قبل از مرحلهٔ بیلد (یا خط نشانهٔ #INSERT-ARGS) مشکل حل میشود.
دیتابیس را داخل پروژه بالا بیاورم یا جدا؟
جدا. فایلسیستم کانتینر با هر دیپلوی عوض میشود و دیتابیس داخل کانتینر ممکن است داده را با خودش ببرد. دیتابیس مدیریتشده بکاپ شبانه دارد و با آدرس داخلی به پروژه وصل میشود؛ همان آدرس را از متغیر محیطی بخوان و هیچوقت localhost ننویس.
هزینهٔ دیپلوی روی PaaS چقدر است؟
ساعتی و بهازای منابع مصرفی، بدون حداقل مصرف ماهانه. اپی با ۲ vCPU، ۴ گیگ رم و ۲۰ گیگ دیسک در آبان ۱۴۰۵ حدود ۱٬۸۲۰ تومان در ساعت بود، یعنی نزدیک ۱٬۳۱۰٬۴۰۰ تومان برای یک ماه روشن بودن کامل؛ هر بیلد هم در کلاستر ایران حدود ۲ هزار تومان است. هنگام ثبتنام هم ۱۰۰ هزار تومان اعتبار اولیه میگیری.
منابع
- مستندات PaaS لکسویا، تایپهای پروژه، پورت، متغیرهای محیطی و دیپلوی خودکار.
- ARG و ENV در Docker، توضیح رسمی تفاوت متغیر بیلد و متغیر اجرا.
- اصل Config در Twelve-Factor App؛ تنظیمات باید در محیط باشد، نه در کد.
مطالب مرتبط
- سرویس PaaS چیست؟ برای تفاوت PaaS با IaaS و اینکه کِی باید VPS بگیری.
- مقایسه PaaS ایرانی اگر هنوز بین پلتفرمها تصمیم نگرفتهای.
- دیپلوی چیست؟ برای مراحل دیپلوی، برگشت به عقب و چکلیست امنیتی.
- Docker چیست؟ اگر میخواهی Dockerfile را از پایه یاد بگیری.
- دیپلوی Laravel و سرور مورد نیاز Next.js برای مسیرهای فریمورکمحور.