آموزش نصب و میزبانی Supabase روی سرور شخصی با Docker
راهنمای کامل اجرای Supabase روی VPS با Docker Compose. یاد بگیرید چگونه مقادیر فایل .env را امن کنید، 11 سرویس اصلی را مدیریت کرده و بدون حذف دیتابیس، پشته خود را آپدیت کنید.
آنچه میسازید
Self-hosting سرویس Supabase به معنای اجرای پشته رسمی Docker Compose روی سرور شخصی شماست: Postgres، یک REST API در مقابل آن، سرویس احراز هویت، ذخیرهسازی فایل، وبسوکتهای Realtime و داشبورد Studio. شما یک مخزن را clone میکنید، یک فایل .env را ویرایش میکنید و یازده کانتینر را اجرا میکنید که در کنار هم مانند یک پروژه Supabase تحت کنترل شما عمل میکنند.
نصب این پشته کوتاه است. بخشی که معمولاً دچار مشکل میشود، فایل .env است. این فایل با مقادیر پیشفرض (secrets) عرضه میشود که در مخزن عمومی منتشر شدهاند؛ اگر پشتهای با این مقادیر پیشفرض اجرا شود، برای هر کسی که آن را پیدا کند در دسترس خواهد بود. این راهنما به بررسی مقادیری که باید جایگزین کنید، کاربرد هر سرویس، میزان حافظه مورد نیاز واقعی برای این پشته و نحوه بهروزرسانی آن بدون حذف دیتابیس میپردازد.
اگر با Docker Compose آشنا نیستید، ابتدا مبانی Docker Compose روی یک VPS را مطالعه کنید. تمام موارد زیر فرض میکنند که دستور docker compose version هماکنون یک نسخه را نمایش میدهد.
محتوای واقعی این پشته (stack)
Supabase یک برنامه واحد نیست. فایل Compose مجموعهای از سرویسهای مجزا را روی یک شبکه راهاندازی میکند. شناخت هر یک از این سرویسها باعث میشود بهجای مواجهه با لیستی از نام کانتینرها، بتوانید آنها را عیبیابی کنید.
dbهمان PostgreSQL است که افزونههای Supabase روی آن بارگذاری شدهاند. تمام سرویسهای دیگر با این سرویس در ارتباط هستند. اگر این کانتینر در وضعیت ناسالم (unhealthy) باشد، سایر سرویسها نیز از کار میافتند.api-gwدروازه (gateway) API است. از زمان انتشار اوت 2026، این سرویس از Envoy استفاده میکند؛ پیش از آن، سرویسی به نامkongبود و Kong همچنان بهعنوان یک جایگزین اختیاری در دسترس است. این سرویس روی پورت 8000 گوش میدهد و درخواستهای/rest/v1/،/auth/v1/و/storage/v1/را به backend مناسب هدایت میکند. این تنها کانتینری است که باید در معرض اینترنت قرار دهید.restهمان PostgREST است. این سرویس طرحواره (schema) Postgres شما را میخواند و آن را بهعنوان یک REST API ارائه میدهد؛ بنابراین یک جدول جدید بدون نیاز به کدنویسی، به یک endpoint جدید تبدیل میشود.authهمان GoTrue است. این سرویس توکنهای وب JSON یا همان JWT را صادر میکند که هویت کاربران شما را تأیید میکنند.storageوimgproxyمدیریت آپلود فایل و تغییر اندازه تصاویر را بر عهده دارند.realtimeتغییرات پایگاه داده را از طریق websockets استریم میکند.studioوmetaداشبورد و API مدیریتیِ پشت آن هستند.functionsمحیط اجرای edge مبتنی بر Deno وsupavisorمدیریتکننده اتصال (connection pooler) برای Postgres است.
لاگها و تحلیلها (Logflare به همراه vector) از ژوئن 2026 از فایل پیشفرض Compose حذف شدند. این موارد اکنون در یک فایل جایگزین اختیاری قرار دارند که میتوانید آن را با sh run.sh config add logs فعال کنید؛ این همان روش لایهبندی فایلهای Compose است که در اجرای یک پشته از چندین فایل Compose توضیح داده شده است.
دلیل ارقام مربوط به منابع در ادامه، همین لیست است. شما فقط یک پایگاه داده را اجرا نمیکنید، بلکه در حال اجرای یک پایگاه داده به همراه ده سرویس پشتیبان هستید.
تخمین منابع: برنامهریزی برای 8 GB رم
این پشته (stack) در یک نصب تازه در ژوئیه 2026، پیش از اضافه شدن دادهها یا ترافیک شما، حدود 2.5 تا 3 GB از حافظه resident را اشغال میکند. در این اندازهگیری، سرویس تحلیل (analytics) و پردازش Node.js مربوط به Studio، دو مصرفکننده اصلی بودند. از آنجا که بخش تحلیل دیگر بخشی از پشته پیشفرض نیست، تنها در صورتی حافظه مصرف میکند که قابلیت override لاگها را فعال کنید. یک سرور 2 GB کانتینرها را اجرا میکند، اما سپس یکی از آنها توسط مکانیزم OOM Killer هسته سیستمعامل متوقف میشود؛ نشانه این وضعیت، گیر کردن کانتینر در حلقه راهاندازی مجدد با کد خروج 137 است. مستندات خودِ Supabase برای self-hosting که در اکتبر 2026 بررسی شد، حداقل 4 GB رم، 2 هسته CPU و 40 GB فضای SSD را به عنوان حداقل، و 8 GB رم، 4 هسته و 80 GB فضا را به عنوان مقدار پیشنهادی ذکر کردهاند.
برای هر سرویسی که به آن وابستگی دارید، 8 GB رم و 4 vCPU اختصاص دهید. مقدار 4 GB برای یک نمونه توسعه شخصی کافی است، به شرطی که بپذیرید اجرای همزمان یک کوئری سنگین و کار با Studio کند خواهد بود. فضای دیسک نیز اهمیت دارد، زیرا Postgres، حجم ذخیرهسازی (storage volume) و دادههای لاگ (در صورت فعالسازی) همگی در دایرکتوری پروژه قرار میگیرند. با 40 GB شروع کنید و میزان مصرف را زیر نظر بگیرید. شمارش سرویسها پیش از انتخاب پلن، عادتی است که برای هر سرویس self-hosted ارزش حفظ کردن دارد، چرا که PhotoPrism و Immich نیز حداقلهای رم مشخصی دارند: صفحه شروع سریع Immich که در اکتبر 2026 بررسی شد، حداقل 6 GB رم و 2 هسته CPU را درخواست میکند. اگر این منابع بیش از نیاز فعلی پروژه شماست، آن را با آنچه پلن ابری رایگان Supabase واقعاً شامل میشود مقایسه کنید، زیرا یک برنامه کوچک که حجم آن زیر 500 MB باقی میماند و هرگز برای یک هفته غیرفعال نمیماند، ممکن است بدون هزینه در آنجا اجرا شود.
نصب: کلون کردن مخزن رسمی
مسیر پشتیبانیشده، دایرکتوری docker را از مخزن اصلی به دایرکتوری پروژه شخصی شما کپی میکند. این جداسازی اهمیت دارد، زیرا به این معنی است که یک git pull در آینده نمیتواند .env شما را بازنویسی کند.
git clone --depth 1 https://github.com/supabase/supabase
mkdir supabase-project
cp -rf supabase/docker/* supabase-project
cp supabase/docker/.env.example supabase-project/.env
cd supabase-project
docker compose pullدستور docker compose pull چندین گیگابایت تصویر را دانلود میکند. این عملیات باید با علامتگذاری تمام سرویسها به عنوان Pulled به پایان برسد. خطای manifest unknown در اینجا به این معنی است که تگ تصویر پینشده در منبع اصلی حذف شده است؛ راه حل این است که به جای ویرایش دستی تگها، نسخه جدیدتری از مخزن را دریافت (pull) کنید.
رازهایی که باید پیش از اولین اجرا تغییر دهید
این کار را پیش از راهاندازی stack انجام دهید، نه بعد از آن. چندین مورد از این مقادیر در اولین بوت در دادهها نوشته میشوند، بنابراین تغییر آنها در مراحل بعدی به معنای بازنشانی (reset) پایگاه داده است.
این مخزن شامل یک تولیدکننده است که تمام مقادیر را بهدرستی ایجاد میکند، از جمله دو کلید API که باید با JWT secret جدید شما امضا شوند.
sh utils/generate-keys.sh --update-envاین اسکریپت مقادیر جدیدی برای JWT_SECRET، ANON_KEY، SERVICE_ROLE_KEY، SECRET_KEY_BASE، REALTIME_DB_ENC_KEY، VAULT_ENC_KEY، PG_META_CRYPTO_KEY، توکنهای Logflare، کلیدهای پروتکل S3 و MINIO_ROOT_PASSWORD در فایل .env مینویسد. از اکتبر 2026، این اسکریپت مقادیر تصادفی برای POSTGRES_PASSWORD و DASHBOARD_PASSWORD نیز تولید میکند. این اسکریپت به openssl نیاز دارد که در هر ایمیج استاندارد Ubuntu موجود است. از آنجا که این اسکریپت اکنون رمز عبور پایگاه داده را نیز بازنویسی میکند، آن را فقط پیش از اولین اجرا با --update-env اجرا کنید و هرگز روی stackای که حاوی داده است، به کار نبرید.
دو مقدار همچنان ارزش بررسی دستی در .env را دارند:
POSTGRES_PASSWORD. اسکریپت آن را با مقادیر هگز تصادفی پر میکند که امن است. اگر آن را با مقدار دلخواه خود جایگزین میکنید، فقط از حروف و اعداد استفاده کنید. علائم نگارشی در اینجا باعث شکستن رشتههای اتصال (connection strings) میشود که چندین سرویس با ترکیب رشتهها میسازند؛ خطای حاصل به صورت خطای احراز هویت (authentication error) ظاهر میشود، نه خطای تجزیه (parsing error)، که باعث میشود کاربران در مسیر اشتباهی به دنبال علت بگردند.DASHBOARD_USERNAMEوDASHBOARD_PASSWORD. اینها اعتبارنامههای احراز هویت پایه برای Studio هستند. اسکریپت یک رمز عبور تصادفی تنظیم میکند اما نام کاربری را روی مقدار پیشفرضsupabaseباقی میگذارد. رمز عبور پیشفرض ارائهشده دقیقاًthis_password_is_insecure_and_should_be_updatedاست، بنابراین اگر اسکریپت را اجرا نکردهاید، همین حالا آن را تغییر دهید.
درک کنید که چرا ANON_KEY و SERVICE_ROLE_KEY را نمیتوان ابداع کرد. هر دو JWTهایی هستند که با JWT_SECRET امضا شدهاند. درگاه (gateway) فقط کلیدهای دقیق موجود در .env را میپذیرد و سرویسهای پشت آن، امضا را با JWT_SECRET تایید میکنند، بنابراین کلیدی که مطابقت نداشته باشد با خطای HTTP 401 رد میشود. درگاه پیشفرض Envoy پاسخ 401 Unauthorized را میدهد و همینطور Kong override فعلی که نسخه 3.9 را اجرا میکند و {"message":"Unauthorized"} را برمیگرداند. نصبهای پیش از مارس 2026 از Kong 2.8 استفاده میکردند که پاسخ {"message":"Invalid authentication credentials"} را میدهد. این رایجترین خطای self-hosting است: اپراتور JWT_SECRET را تغییر داده اما کلیدهای دمو را نگه داشته است. همیشه هر سه را با هم تولید کنید.
با SERVICE_ROLE_KEY مانند رمز عبور root رفتار کنید. این کلید امنیت سطح ردیف (row level security) را بهطور کامل دور میزند. این کلید فقط به کدهای سمت سرور تعلق دارد و نه جای دیگر.
مقدار SITE_URL را روی آدرس برنامه خود و API_EXTERNAL_URL را روی آدرس عمومی API با پسوند /auth/v1 تنظیم کنید، برای مثال https://supabase.example.com/auth/v1. مقدار SUPABASE_PUBLIC_URL را روی همان آدرس عمومی بدون مسیر تنظیم کنید. سرویس Auth لینکهای تایید ایمیل و callbackهای OAuth خود را از این مقادیر میسازد، بنابراین باقی گذاشتن آنها روی مقادیر پیشفرض localhost باعث میشود هر یک از کاربران شما به ماشین خودشان هدایت شوند.
سپس آنچه را که دارید بررسی کنید:
sh run.sh secretsسرویس را اجرا کرده و از سلامت آن اطمینان حاصل کنید
sh run.sh start
docker compose psrun.sh start دستور docker compose up -d --wait را اجرا میکند، بنابراین تا زمانی که بررسیهای سلامت (health checks) با موفقیت انجام نشوند، دستور به پایان نمیرسد. هر سرویس باید وضعیت running (healthy) یا running را نشان دهد. اولین بوت بین 2 تا 4 دقیقه زمان میبرد، زیرا Postgres پیش از آنکه هر سرویس دیگری بتواند متصل شود، اسکریپتهای اولیه خود را اجرا میکند.
اگر یک کانتینر مدام در حال restart شدن است، لاگهای آن را بر اساس نام سرویس بخوانید:
docker compose logs db
docker compose logs authسرویس Studio سپس روی پورت 8000 در دسترس خواهد بود و از شما نام کاربری و رمز عبوری که برای داشبورد تنظیم کردهاید را درخواست میکند.
پورت 8000 را روی اینترنت عمومی قرار ندهید
درگاه (gateway) روی پورت 8000 از پروتکل HTTP ساده استفاده میکند. تمام کلیدهای API و رمزهای عبور کاربران بهصورت متن آشکار (clear text) در شبکه منتقل میشوند و اعتبارنامههای Studio از نوع basic authentication هستند که در واقع کدگذاری base64 است، نه رمزنگاری.
یک reverse proxy در مقابل آن قرار دهید، TLS (امنیت لایه انتقال) را در همانجا خاتمه دهید و درگاه را به آدرس loopback متصل کنید تا هیچ منبع دیگری نتواند به آن دسترسی پیدا کند. در docker-compose.yml، خط مربوط به پورت در زیر سرویس api-gw به 127.0.0.1:${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp تغییر مییابد و پروکسی درخواستها را به آن هدایت میکند. در نصبهای پیش از تغییر به Envoy در آگوست 2026، همین خط پورت در زیر سرویسی به نام kong قرار دارد. استفاده از Traefik در مقابل چندین برنامه Compose بخش مربوط به گواهیها را پوشش میدهد. همان پروکسی در نهایت در مقابل تمام سرویسهای دیگر روی سرور قرار میگیرد؛ از این پشته گرفته تا چیزی غیرجدی مانند یک کتابخانه Jellyfin که به شکل یک ویدیو کلوپ دهه 90 بازسازی شده است، و هر کدام از آنها به جای باز کردن یک پورت جدید، به یک نام دامنه (hostname) نیاز دارند. اگر داشبورد فقط برای استفاده شخصی شماست، از پروکسی صرفنظر کنید و از طریق یک SSH tunnel به پورت loopback متصل شوید؛ همان رویکردی که سرویس self-hosted open-kritt برای دور نگه داشتن کامل رابط اسکن خود از اینترنت عمومی استفاده میکند.
بقیه پورتها را نیز در فایروال ببندید، زیرا Docker با نوشتن قوانین iptables اختصاصی خود، پورتها را منتشر میکند که پیکربندیهای ساده ufw هرگز آنها را نمیبینند. این تله در چرا کانتینرهای Docker قوانین ufw شما را نادیده میگیرند توضیح داده شده است.
از پایگاه داده نسخه پشتیبان تهیه کنید، نه از دایرکتوری
دادههای Postgres در یک bind mount در مسیر ./volumes/db/data قرار دارند. کپی کردن این دایرکتوری در حالی که container در حال اجراست، منجر به ایجاد یک نسخه ناقص (torn copy) میشود؛ زیرا Postgres عملیات نوشتن را در حافظه موقت (buffer) نگه میدارد و فایلهای روی دیسک تنها در زمان checkpoint با هم سازگار هستند. بازیابی چنین نسخهای معمولاً کار میکند، اما گاهی اوقات آخرین تراکنشها را بدون هیچ هشداری از دست میدهد که بدترین حالت ممکن برای یک نسخه پشتیبان است.
بهجای آن از dump استفاده کنید. دستور pg_dumpall داخل container اجرا میشود و یک snapshot سازگار ایجاد میکند:
docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sqlپیش از آنکه به فایل اعتماد کنید، بررسی کنید که خالی نباشد. سپس این dumpها را طبق یک زمانبندی از سرور خارج کنید؛ این همان کاری است که نسخههای پشتیبان رمزنگاریشده خارج از سایت با restic برای آن طراحی شده است. یک dump زمانبندیشده که بیسروصدا شکست میخورد، هیچ تفاوتی با نداشتن نسخه پشتیبان ندارد؛ بنابراین تنظیم کنید که cron یا systemd job در صورت خروج با کد غیرصفر، یک هشدار به گوشی شما ارسال کند. همزمان از .env خود نیز نسخه پشتیبان تهیه کنید. از دست دادن JWT_SECRET به این معنی است که تمام توکنهای صادرشده نامعتبر شده و تمام اسرار رمزنگاریشدهٔ ذخیرهشده غیرقابل خواندن خواهند بود.
فایلهای آپلودشده در ./volumes/storage قرار دارند و چون فایلهای معمولی هستند، کپی کردن ساده آنها مشکلی ندارد.
بهروزرسانی بدون از دست دادن دادهها
Supabase نسخههای image را در docker-compose.yml ثابت نگه میدارد، بنابراین تا زمانی که خودتان اقدام نکنید، هیچچیز تغییر نمیکند. ثابت نگه داشتن نسخه (Pinning) در هر پشتهای که بهصورت دستی اسمبل میکنید ارزشمند است؛ به همین دلیل است که یک relay سرور RustDesk خودمیزبان، دو image سرور خود را به جای دنبال کردن یک تگ متغیر، ثابت نگه میدارد: ارتقا باید کاری باشد که در صبحی که برای آن وقت دارید، انتخابش کنید. همیشه پیش از هر کاری، یک dump تهیه کنید.
docker compose pull
sh run.sh recreateدستور recreate پشته را متوقف کرده و دوباره آن را با imageهای جدید اجرا میکند. دادههای شما باقی میمانند زیرا در bind mountهای روی میزبان قرار دارند، نه داخل containerها. پیش از جهش به نسخه اصلی (major version)، CHANGELOG.md را در مخزن مطالعه کنید، زیرا ارتقای اصلی Postgres یک مرحله مجزا است که recreate آن را انجام نمیدهد. Postgres 17 از ژوئن 2026 نسخه پیشفرض بوده است و نصب موجود روی Postgres 15 با استفاده از sudo bash utils/upgrade-pg17.sh به آن منتقل میشود؛ این دستور از pg_upgrade برای انتقال دادهها در محل استفاده کرده و در هر مرحله اصلی، درخواست تأیید میکند. این فرآیند به اجرای تمام containerها و فضای دیسک آزاد حداقل دو برابر حجم پایگاه داده شما به اضافه 5 GB نیاز دارد و Supabase همچنان توصیه میکند پیش از شروع، نسخه پشتیبان مستقل خود را تهیه کنید.
برای اعمال تغییرات در خود فایل Compose، دستور sh update.sh را از دایرکتوری پروژه اجرا کنید، یا برای مشاهده تغییرات احتمالی پیش از اعمال، از sh update.sh --dry-run استفاده کنید. این دستور فایلهای upstream را با ویرایشهای شما ادغام میکند، هرگز مقادیر موجود در .env را بازنویسی نمیکند (فقط کلیدهای جدید را اضافه میکند) و پیش از نوشتن، پیکربندی شما را در backups/ آرشیو میکند (البته نه پایگاه داده را). نصب بدون فایل .supabase-version به --from و نسخهای که از آن شروع شده است نیاز دارد. این موضوع بهویژه برای نسخه اوت 2026 اهمیت دارد که سرویس kong را با api-gw جایگزین کرد و به بهروزرسانی docker-compose.yml نیاز دارد. روش دستی نیز همچنان کارآمد است: مخزن upstream را دوباره clone کنید و دایرکتوری docker آن را روی پروژه خود کپی کنید، مراقب باشید که .env بازنویسی نشود.
بازنشانی کامل (Full reset) که همه چیز از جمله پایگاه داده را از بین میبرد و .env را به مقادیر پیشفرض برمیگرداند (نسخه قدیمی به عنوان .env.old نگهداری میشود)، یک اسکریپت جداگانه است و درخواست تأیید میکند:
sh reset.shFAQ
چرا درخواستهای API من خطای "Invalid authentication credentials" را برمیگردانند؟
این پیام دقیقاً از Kong 2.8 میآید؛ همان gateway که در نصبهای پیش از مارس 2026 در stack وجود داشت. Envoy gateway پیشفرض و نسخه فعلی Kong که Kong 3.9 است، همین مشکل را به صورت یک خطای ساده 401 Unauthorized گزارش میدهند. در هر صورت، کلیدی که ارسال کردهاید با ANON_KEY یا SERVICE_ROLE_KEY در .env مطابقت ندارد، یا آن کلیدها با JWT_SECRET که در حال حاضر در .env قرار دارد، امضا نشدهاند. در stackای که هرگز دادهای در آن نبوده، هر سه مورد را با sh utils/generate-keys.sh --update-env دوباره تولید کنید. در stackای که در حال اجراست، اسکریپت را بدون flag اجرا کنید و به سوالات پاسخ منفی دهید تا فقط مقادیر جدید را چاپ کند؛ سپس JWT_SECRET، ANON_KEY و SERVICE_ROLE_KEY را بهصورت دستی در .env کپی کنید و POSTGRES_PASSWORD را تغییر ندهید. سپس sh run.sh recreate را اجرا کنید تا سرویسها مقادیر جدید را بخوانند.
آیا میتوانم Supabase را به صورت self-hosted روی یک VPS با 2 GB رم اجرا کنم؟
خیر، پایداری نخواهد داشت. مستندات رسمی Supabase برای self-hosting از اکتبر 2026، حداقل 4 GB رم را توصیه کردهاند. این stack در جولای 2026 در حالت idle نزدیک به 3 GB رم مصرف میکرد (این اندازهگیری شامل سرویسهای لاگ بود که اکنون اختیاری هستند). یک سرور 2 GB باعث میشود containerها توسط out of memory killer متوقف شوند و شما کد خروج 137 را در docker compose ps مشاهده خواهید کرد. برای محیط production از 8 GB رم استفاده کنید و 4 GB را حداقلِ مورد نیاز برای توسعه شخصی در نظر بگیرید.
آیا Supabase به صورت self-hosted شامل edge functions میشود؟
بله. فایل Compose شامل runtime مبتنی بر Deno برای functions است و هر چیزی را که در مسیر ./volumes/functions قرار دهید، اجرا میکند. این مورد شامل شبکه توزیع جهانی پلتفرم ابری Supabase نیست، بنابراین functions شما فقط روی همان یک سرور و در یک موقعیت جغرافیایی اجرا میشوند.
چگونه مستقیماً به دیتابیس Postgres متصل شوم؟
برای دسترسی به shell تعاملی روی خودِ سرور از docker exec -it supabase-db psql -U postgres استفاده کنید. برای کلاینتهای خارجی، از طریق Supavisor روی پورت 5432 با کاربر postgres.<POOLER_TENANT_ID> و POSTGRES_PASSWORD خود متصل شوید. این پورت را روی اینترنت باز نکنید. از طریق VPN یا SSH tunnel به آن دسترسی پیدا کنید.
چرا لینک ایمیلهای تأیید هویت به localhost اشاره میکنند؟
مقادیر SITE_URL و API_EXTERNAL_URL در .env روی حالت پیشفرض باقی ماندهاند. سرویس auth تمام لینکهای تأیید و بازنشانی رمز عبور را بر اساس این دو مقدار میسازد، بنابراین آدرسی را ارسال میکند که برای آن تنظیم شده است. هر دو را روی URL عمومی و واقعی خود تنظیم کرده و stack را دوباره ایجاد کنید.