آموزش نصب LinkBreeze روی سرور شخصی با Docker Compose
راهنمای کامل راهاندازی LinkBreeze به عنوان جایگزین Linktree با استفاده از Docker و Caddy. یاد بگیرید چگونه با مدیریت یک Volume و تنظیم دقیق تگهای ایمیج، سایت خود را مستقر کنید.
LinkBreeze چیست
LinkBreeze یک جایگزین خودمیزبان (self-hosted) برای Linktree است: یک کانتینر Docker که یک صفحه عمومی «لینک در بیو» و یک داشبورد مدیریتی ارائه میدهد و تمام وضعیت (state) آن در یک فایل SQLite ذخیره میشود. این پروژه تحت مجوز MIT، با استفاده از TypeScript و Next.js نوشته شده و به عنوان یک ایمیج کانتینر در Docker Hub با نام omnirise/linkbreeze و در GHCR با نام ghcr.io/manak-hash/linkbreeze منتشر شده است. برای اجرای آن به یک VPS، یک دامنه با رکورد A که به آن VPS اشاره میکند، باز بودن پورتهای 80 و 443، و Docker Engine به همراه افزونه Compose نیاز دارید.
این راهنما استقراری را پوشش میدهد که مخزن پروژه بهطور رسمی از آن پشتیبانی میکند: Docker Compose پشت یک reverse proxy که گواهیهای TLS خود را دریافت میکند. همچنین به مواردی میپردازیم که باعث خرابی میشوند، زیرا «لینک در بیو» یک URL عمومی است که دیگران روی آن کلیک میکنند و خرابی آن به معنای از دست رفتن کلیک است.
پیش از هر اقدامی، توجه داشته باشید که این پروژه بسیار جدید است.
آیا LinkBreeze برای استفاده در لینک پروفایل عمومی به اندازه کافی بالغ است؟
در اوت 2026، مخزن GitHub این پروژه دارای 178 ستاره، 17 فورک و تنها یک نگهدارنده بود. اولین نسخه تگشده، یعنی v1.0.0، در تاریخ 1 ژوئیه 2026 منتشر شد. این پروژهای است که چند ماه از عمر آن میگذرد، نه چند سال.
در 11 اکتبر 2026، حساب کاربری GitHub نگهدارنده و خود مخزن، هر دو خطای 404 را برگرداندند. در changelog به مشکلاتی در خصوص دیده شدن حساب GitHub اشاره شده است و سورس کد همچنان در mirror رسمی پروژه در GitLab به آدرس gitlab.com/omnirise00/linkbreeze در دسترس است و ایمیج آن نیز در Docker Hub قرار دارد.
دادههای پشت این نمودار
[
{
"week": "2026-06-29",
"releases": 3,
"cumulative": 3
},
{
"week": "2026-07-06",
"releases": 3,
"cumulative": 6
},
{
"week": "2026-07-13",
"releases": 1,
"cumulative": 7
},
{
"week": "2026-07-20",
"releases": 2,
"cumulative": 9
},
{
"week": "2026-07-27",
"releases": 3,
"cumulative": 12
},
{
"week": "2026-08-03",
"releases": 2,
"cumulative": 14
},
{
"week": "2026-08-10",
"releases": 4,
"cumulative": 18
},
{
"week": "2026-08-17",
"releases": 1,
"cumulative": 19
},
{
"week": "2026-08-24",
"releases": 1,
"cumulative": 20
},
{
"week": "2026-08-31",
"releases": 0,
"cumulative": 20
},
{
"week": "2026-09-07",
"releases": 0,
"cumulative": 20
},
{
"week": "2026-09-14",
"releases": 1,
"cumulative": 21
},
{
"week": "2026-09-21",
"releases": 1,
"cumulative": 22
}
]از زمان v1.0.0، این پروژه 22 نسخه تگشده را در طول 13 هفته تقویمی منتشر کرده است که آخرین آن v1.4.1 در تاریخ 23 سپتامبر 2026 بوده است. سرعت انتشار کاهش یافته است: 17 مورد از این نسخهها تا 14 اوت منتشر شدند و از 11 اکتبر 2026، هیچ نسخه جدیدی پس از v1.4.1 تگ نشده است.
این موضوع را به عنوان دو واقعیت مجزا در نظر بگیرید. نگهدارنده فعال است و باگها ظرف چند روز برطرف میشوند. با این حال، اسکیما و تنظیمات پیشفرض همچنان در حال تغییر هستند، بنابراین نمونهای که مستقر میکنید و آن را به حال خود رها میکنید، با گذشت زمان فاصله زیادی با کدهای جدید پیدا خواهد کرد.
مجوز (License) شما را در بدترین حالت محافظت میکند. MIT به همراه یک ایمیج کانتینر و یک فایل SQLite روی دیسک شخصیتان به این معنی است که اگر توسعه متوقف شود، آنچه دارید همچنان به کار خود ادامه میدهد. چیزی که شما را در برابر آن محافظت نمیکند، یک وباپلیکیشن عمومی است که دریافت اصلاحیههای امنیتی را متوقف کرده و به مرور زمان به یک ریسک تبدیل میشود. این سرویس را به عنوان چیزی مستقر کنید که قصد دارید آن را بهروز نگه دارید و روال پشتیبانگیری زیر را از همان روز اول فعال کنید.
تگ ایمیج را ثابت (Pin) کنید و از اجرای latest خودداری کنید
گردش کار فعلی انتشار، دو تگ برای هر نسخه به Docker Hub و GHCR ارسال میکند: latest و تگ git دقیقاً همانطور که ارسال شده است، که شامل v میشود. بنابراین تگ ثابت برای نسخه v1.4.1 برابر با docker.io/omnirise/linkbreeze:v1.4.1 است. نسخههای پیش از v1.4.0 به شکل متفاوتی تگگذاری میشدند: گردش کار، v را حذف میکرد و آن نسخهها فقط در GHCR به صورت اعداد خالص مانند ghcr.io/manak-hash/linkbreeze:1.2.7 وجود دارند. اگر تگ را با فرمت اشتباه برای آن نسخه بنویسید، Docker خطای manifest unknown را گزارش میدهد، زیرا آن تگ هرگز ارسال نشده است. Docker Hub که اکنون در README به عنوان مسیر پیشفرض pull استفاده میشود، فقط شامل latest و v1.4.1 است.
آن را ثابت کنید زیرا latest تغییر میکند. با توجه به زمانبندی در جدول بالا، یک docker compose pull در برابر latest، یک بهروزرسانی بررسینشده از صفحهای است که مخاطبان شما از آن استفاده میکنند. با استفاده از یک تگ ثابت، بهروزرسانی زمانی رخ میدهد که شما فایل را ویرایش کنید.
یک نکته دیگر درباره ایمیج. از نسخه v1.4.0 به بعد، این ایمیج یک لیست manifest چندمعماری برای linux/amd64 و linux/arm64 است که هر کدام روی یک runner بومی ساخته شدهاند. اگر از یک VPS با معماری ARM به جای x86 استفاده میکنید، همان تگ، build مربوط به arm64 را دریافت میکند و نیازی به کامپایل کردن هیچ چیزی روی سرور نیست. نسخههای تا v1.3.2 فقط برای linux/amd64 منتشر شدهاند و بردهای 32 بیتی arm/v7 عمداً کنار گذاشته شدهاند، بنابراین یک تگ ثابت قدیمی یا یک برد 32 بیتی همچنان با خطای no matching manifest مواجه میشود.
استقرار LinkBreeze پشت Caddy با TLS خودکار
Caddy گواهیها را بهطور خودکار از Let's Encrypt درخواست و تمدید میکند، بنابراین TLS (امنیت لایه انتقال) نیازی به مرحله جداگانه برای مدیریت گواهی ندارد. کل فرآیند استقرار شامل سه فایل در یک دایرکتوری است.
ابتدا secret را تولید کنید:
mkdir -p ~/linkbreeze && cd ~/linkbreeze
printf 'SECRET_KEY=%s\n' "$(openssl rand -hex 32)" > .env
chmod 600 .envSECRET_KEY کوکی نشست مدیریت را امضا کرده و به هش بازدیدکنندگان تحلیلگر نمک (salt) اضافه میکند؛ از نسخه 1.4.0 به بعد، این مقدار کلیدی را که اسرار احراز هویت دو مرحلهای (TOTP) ذخیرهشده را رمزنگاری میکند، مشتق مینماید. فایل compose منتشرشده در مخزن، این مقدار را بهصورت پیشفرض روی ${SECRET_KEY:-changeme-in-production} تنظیم میکند، بنابراین اگر این مرحله را نادیده بگیرید، نمونه شما با کلید امضای نشستی اجرا میشود که در مخزن عمومی پروژه درج شده است. آن را پیش از اولین اجرا تنظیم کنید، زیرا تغییر آن در آینده باعث خروج شما از سیستم و بازنشانی نمک تحلیلگر میشود.
فایل docker-compose.yml را بنویسید:
services:
linkbreeze:
image: docker.io/omnirise/linkbreeze:v1.4.1
restart: unless-stopped
volumes:
- linkbreeze-data:/app/data
environment:
- DATABASE_PATH=/app/data/linkbreeze.db
- SECRET_KEY=${SECRET_KEY}
- BASE_URL=https://links.example.com
networks:
- linkbreeze-net
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
- linkbreeze-net
networks:
linkbreeze-net:
volumes:
linkbreeze-data:
caddy-data:
caddy-config:BASE_URL اختیاری است اما تنظیم آن توصیه میشود: این مقدار آدرس عمومی واقعی برنامه را به آن اعلام میکند تا درخواستی که با هدر جعلی Host میرسد، باعث نشود برنامه لینکهایی به دامنه شخص دیگری تولید کند.
فایل Caddyfile را در کنار آن، با دامنه اختصاصی خود ایجاد کنید:
links.example.com {
encode zstd gzip
reverse_proxy linkbreeze:3000
}Caddy بهطور پیشفرض X-Forwarded-For و X-Forwarded-Proto را در درخواستهای پروکسیشده تنظیم میکند که تحلیلگر به آنها وابسته است. سرویس را بالا بیاورید:
docker compose up -d
docker compose ps
docker compose logs -f caddydocker compose ps باید کانتینر LinkBreeze را در وضعیت healthy نشان دهد. این ایمیج دارای healthcheck داخلی به نام wget --spider -q http://127.0.0.1:3000/api/health است، بنابراین نیازی به افزودن آن ندارید. healthcheck را از نمونه Caddy موجود در مخزن کپی نکنید: آن نمونه curl را فراخوانی میکند، در حالی که ایمیج بر پایه node:22-alpine ساخته شده که شامل busybox wget است و curl ندارد. آن کانتینر در حالی که صفحات را بهدرستی سرو میکند، وضعیت unhealthy را گزارش میدهد.
آدرس https://links.example.com را در مرورگر باز کنید. اولین بازدید شما را به ویزارد راهاندازی در /setup هدایت میکند که حساب کاربری تکمدیر را ایجاد مینماید. پس از آن، داشبورد در /dashboard و فرم ورود در /login در دسترس خواهد بود. آن حساب کاربری مختص همین نمونه است و هیچ قلاب single sign-on در برنامه وجود ندارد؛ بنابراین اگر میخواهید داشبورد با همان ورود سایر سرویسهای میزبانیشده شما کار کند، باید از یک forward auth proxy در مقابل آن استفاده کنید، مانند یک نمونه Authentik خودمیزبان.
توجه کنید که فایل compose چه کاری انجام نمیدهد: این فایل هرگز پورت 3000 را منتشر نمیکند. فقط Caddy روی رابط عمومی گوش میدهد. اگر با سینتکس فایل Compose آشنا نیستید، اصول Docker Compose برای VPS بخشهایی را که این فایل فرض گرفته است پوشش میدهد، و اگر در حال حاضر سرویس دیگری در مقابل دارید، مقایسه Nginx، Caddy و Traefik تغییرات لازم را توضیح میدهد. این مخزن نمونههای کاری برای Nginx با Certbot، Traefik و تونل Cloudflare ارائه میدهد.
محل ذخیره دادهها و محتویات یک نسخه پشتیبان
DATABASE_PATH به /app/data/linkbreeze.db اشاره دارد. آواتارهای آپلود شده و تصاویر بندانگشتی لینکها در کنار آن در /app/data/uploads نوشته میشوند. هر دو در volume نامگذاریشده linkbreeze-data قرار دارند، بنابراین واحد پشتیبانگیری، خودِ volume است، نه فقط فایل پایگاه داده. اگر فایل را بدون دایرکتوری آپلودها بازیابی کنید، تمام تصاویر موجود در صفحه با خطای 404 مواجه خواهند شد.
همه چیزهای دیگر در همان یک پایگاه داده قرار دارند: صفحات، لینکها، تنظیمات، قالب، مشترکین ایمیل و ردیفهای تحلیل (analytics).
نسخه کپی را در حالی که container متوقف است تهیه کنید:
docker compose stop linkbreeze
docker compose cp linkbreeze:/app/data ./backup-$(date +%F)
docker compose start linkbreezeابتدا container را متوقف کنید، زیرا کپی کردن یک پایگاه داده SQLite در حالی که فرآیندی در حال نوشتن روی آن است، ممکن است منجر به ثبت یک تراکنش نیمهتمام شود و فایل کپیشده به صورت خراب (corrupt) باز شود. در حین اجرای کپی، صفحه در دسترس نخواهد بود. بازیابی نیز همان حرکت به صورت معکوس است:
docker compose stop linkbreeze
docker compose cp ./backup-2026-08-14/. linkbreeze:/app/data
docker compose start linkbreeze
docker compose logs -f linkbreezeداشبورد همچنین یک خروجی JSON ارائه میدهد که از /api/backup با مسیر linkbreeze-backup-YYYY-MM-DD.json در دسترس است. این خروجی شامل پروفایل، لینکها و بخشهای آنها، تنظیمات، قالبهای ذخیرهشده و ورودیهای فونت سفارشی است. این خروجی شامل تاریخچه تحلیلها، مشترکین ایمیل، حساب کاربری مدیر یا فایلهای آپلود شده نیست و بازیابی آن باعث حذف ردیفهای فعلی در شش جدولی میشود که شامل آنهاست، پیش از آنکه دادههای فایل جدید را جایگزین کند. از این قابلیت به عنوان یک snapshot پیکربندی برای انتقال میزبان یا بازگرداندن اشتباهات ویرایشی استفاده کنید. کپیِ volume، نسخه پشتیبان اصلی است.
دو قانون ذخیرهسازی در اینجا نیز مانند هر جای دیگری که در حال اجرای SQLite در محیط عملیاتی روی یک VPS هستید، اعمال میشود. پایگاه داده را روی دیسک محلی نگه دارید، زیرا قفلگذاری (locking) در SQLite روی سیستمفایلهای شبکهای غیرقابلاطمینان است و خرابی صفحات (corrupt page) نتیجه آن خواهد بود. و اگر volume نامگذاریشده را با یک host bind mount جایگزین کردید، ابتدا مالکیت دایرکتوری میزبان را با chown تغییر دهید: container با کاربر غیر-root یعنی node با uid 1000 در node:22-alpine اجرا میشود و دایرکتوری که توسط root ایجاد شده باشد برای آن قابل نوشتن نیست؛ در نتیجه برنامه نمیتواند پایگاه داده را باز کند و container در هنگام شروع کار متوقف میشود. مقاله Bind mounts در مقابل named volumes در Compose این مبحث را به طور کامل پوشش میدهد.
تحلیل دادهها و بنر رضایت که به آن نیازی ندارید
این همان قابلیتی است که میزبانی شخصی (self-hosting) صفحهای را که میتوانید در جای دیگری بهرایگان داشته باشید، توجیه میکند.
تحلیل دادهها بدون استفاده از کوکی انجام میشود. هیچ کوکی برای بازدیدکننده تنظیم نمیشود و هیچ اسکریپت شخصثالثی در صفحه عمومی بارگذاری نمیگردد. بازدیدکننده با استفاده از یک هش SHA-256 از آدرس IP، رشته user agent و یک salt شناسایی میشود که به 16 کاراکتر هگزادسیمال محدود شده است. خودِ salt، هشی از تاریخ UTC فعلی و SECRET_KEY شماست؛ بنابراین در نیمهشب UTC تغییر میکند و نمیتوان هشهای دیروز را با امروز مطابقت داد. آدرس IP خام هرگز در پایگاه داده نوشته نمیشود.
کلیکها روی سرور شمارش میشوند. هر لینک http در صفحه عمومی به /go/<id> در دامنه خودتان اشاره میکند که کلیک را ثبت کرده و سپس با یک redirect 302 به مقصد اصلی پاسخ میدهد. بنابراین شمارش برای خوانندگانی که JavaScript را غیرفعال کردهاند و همچنین در مرورگرهای درونبرنامهای (in-app browsers) که درخواستهای پسزمینه را مسدود میکنند، بهدرستی کار میکند. بازدیدهای صفحه از طریق /api/track ثبت میشوند.
دو مورد استثنا ارزش دانستن دارند. درخواستی که دارای session معتبر مدیریت باشد نادیده گرفته میشود، بنابراین ویرایش صفحه شخصیتان باعث افزایش کاذب آمار نمیشود. user agentهای شناختهشدهٔ خزندهها (crawler) نیز نادیده گرفته میشوند.
در مورد رضایت: هیچ چیزی روی دستگاه خواننده ذخیره نمیشود و کوکی ذخیرهشده روی دستگاه خواننده همان چیزی است که بنر کوکی برای آن درخواست اجازه میکند. تعهدات شما همچنان به محل زندگی خوانندگانتان بستگی دارد، پس آنها را بررسی کنید، اما در اینجا هیچ کوکی ردیابی برای افشا وجود ندارد و هیچ شخصثالثی دادهها را دریافت نمیکند.
یک نکته که ممکن است باعث تعجب شود: اگر SECRET_KEY را تغییر دهید، salt روزانه نیز با آن تغییر میکند؛ بنابراین از آن لحظه به بعد، هر بازدیدکننده بازگشتی بهعنوان بازدیدکننده جدید شمارش میشود.
چرا ستون کشور در بخش تحلیلها خالی است؟
به این دلیل که هیچ بخشی در stack شما هدر کشور را تنظیم نمیکند. LinkBreeze کشور را از طریق هدرهای پروکسی مانند cf-ipcountry و x-vercel-ip-country تشخیص میدهد. روی یک VPS که پشت Caddy یا Nginx خودتان قرار دارد، هیچکدام از این هدرها وجود ندارند؛ بنابراین کشور به صورت null ثبت شده و گزارش تفکیکی خالی میماند. هیچ پایگاه داده GeoIP درون container وجود ندارد.
برای پر کردن آن دو راه وجود دارد. Cloudflare را جلوی دامنه قرار دهید که به هر درخواست پروکسیشده، cf-ipcountry را اضافه میکند. یا یکی از این هدرها را در reverse proxy خودتان از طریق یک جستجوی GeoIP محلی تنظیم کنید.
تلهٔ مرتبط با این موضوع خطرناکتر است، پس آن را بررسی کنید. هندلرهای کلیک و بازدید، آدرس کلاینت را ابتدا از X-Forwarded-For و سپس X-Real-IP میخوانند و در صورتی که هیچکدام از این هدرها موجود نباشد، به 0.0.0.0 بازمیگردند. اگر پورت 3000 را بدون هیچ پروکسی مستقیماً روی اینترنت باز کنید، تمام بازدیدکنندگان به یک مقدار هش میشوند؛ این یعنی تعداد بازدیدکنندگان یکتا همیشه 1 باقی میماند و محدودیت نرخ (rate limit) 60 رویداد در دقیقه برای هر IP، به کل مخاطبان شما به صورت همزمان اعمال میشود. با قرار گرفتن پشت دستورالعمل reverse_proxy که در بالا ذکر شد، Caddy هدر را برای شما تنظیم میکند و هر دو مشکل برطرف میشوند.
وارد کردن اطلاعات از Linktree و مواردی که منتقل نمیشوند
ویزارد مهاجرت در داشبورد، یک URL پروفایل عمومی یا یک فایل خروجی را میپذیرد. این ابزار صفحات linktr.ee، bento.me، lnk.bio، tap.link، hopp.bio، beacons.ai، solo.to، linkfly، mssg.me و LittleLink و همچنین خروجیهای عمومی HTML و JSON را شناسایی میکند. برای URLهای Linktree یا Bento، این ابزار دادههای __NEXT_DATA__ JSON که در آن صفحات تعبیه شدهاند را میخواند. برای صفحات ایستا، تگهای anchor خوانده میشوند.
مواردی که منتقل میشوند عبارتند از: عنوان، URL، توضیحات و تصویر هر لینک، اینکه آیا لینک یک پروفایل اجتماعی است یا خیر، و همچنین نام نمایشی، بیوگرافی و آواتار شما. پیش از آنکه هر دادهای در دیتابیس نوشته شود، شما انتخاب میکنید که کدامیک از لینکهای یافتشده حفظ شوند.
مواردی که منتقل نمیشوند عبارتند از: تاریخچه تحلیلها (analytics)، تم و چیدمان، مشترکین ایمیلی، تاریخهای زمانبندیشده برای انتشار، و هر چیزی که پلتفرم قدیمی پشت سیستم ورود (login) خود نگه میدارد. برنامهریزی کنید که ظاهر صفحه را بهصورت دستی بازسازی کنید و بپذیرید که تاریخچه کلیکهای قدیمی در همان سرویس قبلی باقی میماند.
ابزار واردکننده، URL را از سرور شما فراخوانی میکند، نه از مرورگرتان؛ بنابراین آدرسهایی که عمومی نیستند را نمیپذیرد. خطای Private/local URLs are not allowed به این معناست که شما آدرسی را در شبکه داخلی خود وارد کردهاید و این امتناع عمدی است: بدون این محدودیت، هر کسی که به داشبورد دسترسی داشته باشد میتواند از سرور شما برای بررسی ماشینهایی استفاده کند که فقط سرور شما به آنها دسترسی دارد. سایر پیامهایی که ممکن است مشاهده کنید عبارتند از Only http and https URLs are allowed، Request timed out و Response too large.
عملیات Scraping به ساختار HTML دیگران وابسته است. اگر ویزارد در صفحهای که بهوضوح دارای لینک است چیزی پیدا نکرد، به این دلیل است که آن پلتفرم از زمان نوشتهشدن parser، ساختار HTML خود را تغییر داده است. بهجای منتظر ماندن برای اصلاح، لینکها را بهصورت دستی اضافه کنید. اگر آنچه واقعاً نیاز دارید لینکهای کوتاه قابلاندازهگیری است و نه یک صفحه پروفایل، یک سرویس کوتاهکننده لینک self-hosted مانند Shlink این کار را انجام میدهد و بهخوبی روی همان سرور اجرا میشود.
بهروزرسانی یک deployment ثابت (pinned)
# edit the image tag in docker-compose.yml, then
docker compose pull
docker compose up -d
docker compose logs -f linkbreezeمهاجرتهای Schema بهطور خودکار هنگام شروع container اجرا میشوند. هیچ روش مستندی برای اجرای معکوس آنها وجود ندارد، بنابراین ابتدا از volume نسخه پشتیبان تهیه کنید. ارتقایی که نمیتوانید آن را بازگردانید، تنها زمانی ایمن است که بتوانید وضعیت پیش از آن را بازیابی کنید.
داشبورد بهگونهای طراحی شده است که در صورت وجود نسخه جدیدتر، یک بنر نمایش دهد. نسخههای منتشرشده با دریافت یک فایل کوچک نسخه از مخزن GitHub پروژه، حداکثر هر 24 ساعت یکبار، این موضوع را بررسی میکنند و هیچ اطلاعاتی درباره instance شما ارسال نمیکنند. در changelog ذکر شده است که نسخههای v1.2.0 تا v1.4.0 آدرسی را فراخوانی میکردند که پاسخ 404 برمیگرداند و بنر هرگز نمایش داده نمیشد؛ تا تاریخ 11 اکتبر 2026، آن فایل در GitHub همچنان پاسخ 404 میداد. ورودی بعدی در changelog که هنوز منتشر نشده است، این بررسی را به linkbreeze.omnirise.dev/latest-version.json منتقل میکند. تا زمانی که نسخهای با این تغییر را اجرا نکردهاید، منتظر بنر نباشید: خودتان changelog را در linkbreeze.omnirise.dev/changelog بررسی کنید. پیش از تغییر تگ، یادداشتهای انتشار (release notes) را مطالعه کنید، زیرا در این مرحله از پروژه، یک نسخه minor میتواند تنظیمات پیشفرضی که به آنها متکی هستید را تغییر دهد.
حالتهای شکست و پیامهایی که مشاهده خواهید کرد
manifest unknown هنگام pull کردن. تگ با فرمت اشتباه برای آن release نوشته شده است. از نسخه v1.4.0 به بعد، تگها شامل v هستند، بنابراین از :v1.4.1 استفاده کنید. نسخههای GHCR تا v1.3.2 از اعداد ساده مانند :1.2.7 استفاده میکنند و Docker Hub فقط شامل latest و v1.4.1 است.
no matching manifest for linux/arm64/v8 in the manifest list entries. شما یک release قدیمیتر از v1.4.0 را پین کردهاید که فقط برای amd64 منتشر شده بود. پین را به v1.4.1 تغییر دهید که دارای build برای arm64 است. یک برد arm/v7 با معماری 32-bit در تمام نسخهها با همین نوع خطا مواجه میشود، زیرا این پلتفرم بهعمد پشتیبانی نمیشود.
کانتینر گزارش unhealthy میدهد در حالی که صفحه بهدرستی بارگذاری میشود. یک healthcheck در فایل compose شما در حال فراخوانی curl است که در image وجود ندارد. آن را حذف کنید و اجازه دهید healthcheck داخلی خودِ image یعنی wget اجرا شود.
Caddy خطای گواهی میدهد یا اصلاً پاسخی نمیدهد. docker compose logs caddy را بررسی کنید. دلایل معمول این است که رکورد A هنوز به این VPS اشاره نمیکند، یا پورت 80 روی فایروال بسته است که مانع از انجام چالش HTTP مربوط به ACME (محیط مدیریت خودکار گواهی) میشود؛ چالشی که Caddy برای اثبات کنترل دامنه از آن استفاده میکند.
تعداد بازدیدکنندگان یکتا روی 1 ثابت مانده است. هیچ پروکسی در حال تنظیم X-Forwarded-For نیست، بنابراین هشِ تمام بازدیدکنندگان یکسان محاسبه میشود.
کانتینر بلافاصله پس از شروع متوقف میشود، در حالی که دیروز کار میکرد. اگر از named volume به host bind mount مهاجرت کردهاید، دایرکتوری داده متعلق به root است و برنامه با uid 1000 اجرا میشود، بنابراین نمیتواند فایل دیتابیس را باز کند. مالکیت دایرکتوری میزبان را با sudo chown -R 1000:1000 اصلاح کنید.
درخواستهای ردیابی با HTTP 429 پاسخ داده میشوند. محدودیت نرخ (throttle) به ازای هر IP روی /api/track و /go/<id> فعال شده است. بازدیدکنندگان همچنان به مقصد خود هدایت میشوند، فقط کلیک آنها شمارش نمیشود.
FAQ
آیا LinkBreeze برای استفاده به عنوان لینک در بیو (link in bio) عمومی آماده است؟
این یک پروژه نوپا است. در اوت 2026، مخزن GitHub آن دارای 178 ستاره، 17 فورک و یک نگهدارنده بود و اولین نسخه آن در 1 ژوئیه 2026 منتشر شد. این پروژه بین آن تاریخ تا نسخه v1.4.1 در 23 سپتامبر 2026، تعداد 22 نسخه تگشده منتشر کرد که بیشتر آنها پیش از اواسط اوت بودند؛ بنابراین رفتار برنامه بهسرعت تغییر کرده است. در 11 اکتبر 2026، حساب GitHub با خطای 404 مواجه شد، در حالی که mirror در GitLab و ایمیج در Docker Hub همچنان در دسترس بودند. مجوز MIT و فایل محلی SQLite به این معناست که حتی در صورت توقف توسعه، صفحه شما همچنان کار خواهد کرد؛ اما یک وباپلیکیشن عمومی بدون وصلههای امنیتی به یک ریسک تبدیل میشود. بنابراین، با این نرمافزار به عنوان ابزاری برخورد کنید که باید بهطور مداوم بهروزرسانی شود، نه نرمافزاری که یکبار نصب و رها شود.
از کدام تگ ایمیج LinkBreeze باید استفاده کنم؟
از تگ نسخه استفاده کنید، برای مثال docker.io/omnirise/linkbreeze:v1.4.1، و آن را بهصورت آگاهانه تغییر دهید. از نسخه v1.4.0 به بعد، گردش کار انتشار، تگ latest و تگ git همراه با پیشوند v را ارسال میکند، بنابراین :1.4.1 بدون v وجود ندارد و Docker پاسخ manifest unknown را برمیگرداند. نسخههای تا v1.3.2 برعکس هستند: فقط اعداد خام در GHCR. این ایمیج از نسخه v1.4.0 برای معماریهای linux/amd64 و linux/arm64 ساخته شده است، بنابراین یک VPS با معماری arm64 آن را مستقیماً و بدون نیاز به build محلی دریافت میکند.
چرا تفکیک کشوری در تحلیلهای LinkBreeze خالی میماند؟
LinkBreeze کشور بازدیدکننده را از هدرهای پروکسی مانند cf-ipcountry یا x-vercel-ip-country میخواند و خود دارای پایگاه داده GeoIP نیست. یک VPS که پشت Caddy یا Nginx شخصی شما قرار دارد، هیچکدام از این هدرها را تنظیم نمیکند، بنابراین کشور به صورت null ذخیره میشود. Cloudflare را جلوی دامنه قرار دهید یا reverse proxy خود را طوری تنظیم کنید که یکی از این هدرها را از طریق یک جستجوی محلی GeoIP مقداردهی کند.
دقیقاً چه چیزی را باید بکآپ بگیرم و چگونه آن را بازیابی کنم؟
از کل volume مسیر linkbreeze-data بکآپ بگیرید، نه فقط فایل پایگاه داده. /app/data/linkbreeze.db شامل تمام لینکها، صفحات، تنظیمات، مشترکین و ردیفهای تحلیل است و /app/data/uploads شامل تصاویر آواتار و بندانگشتی (thumbnail) است که صفحه به آنها ارجاع میدهد. کانتینر را متوقف کنید، دستور docker compose cp linkbreeze:/app/data ./backup-$(date +%F) را اجرا کنید و سپس دوباره آن را استارت بزنید. برای بازیابی، دایرکتوری را به کانتینر متوقفشده بازگردانید و آن را استارت کنید. خروجی JSON از داشبورد، یک snapshot پیکربندی از پروفایل، لینکها، تنظیمات، تمها و ورودیهای فونت است و شامل هیچگونه داده تحلیلی یا تصویری نیست.
آیا وارد کردن اطلاعات از Linktree، تحلیلها و تم من را هم منتقل میکند؟
خیر. ویزارد مهاجرت، عناوین لینکها، URLها، توضیحات و تصاویر را از پروفایل عمومی قدیمی شما به همراه نام نمایشی، بیوگرافی و آواتار میخواند. تاریخچه تحلیلها، تم، مشترکین ایمیلی و تاریخهای انتشار زمانبندیشده منتقل نمیشوند. پس از وارد کردن اطلاعات، ظاهر را در ویرایشگر تم بازسازی کنید و انتظار داشته باشید که تاریخچه کلیکهای شما در پلتفرم قدیمی باقی بماند.