SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-15

آموزش نصب LinkBreeze روی سرور شخصی با Docker Compose

راهنمای کامل راه‌اندازی LinkBreeze به عنوان جایگزین Linktree با استفاده از Docker و Caddy. یاد بگیرید چگونه با مدیریت یک Volume و تنظیم دقیق تگ‌های ایمیج، سایت خود را مستقر کنید.

LinkBreeze چیست

LinkBreeze یک جایگزین self-hosted برای Linktree است: یک کانتینر Docker که یک صفحه عمومی link-in-bio و یک داشبورد مدیریتی ارائه می‌دهد و تمام وضعیت (state) آن در یک فایل SQLite ذخیره می‌شود. این پروژه تحت مجوز MIT، با استفاده از TypeScript و Next.js نوشته شده و به عنوان ghcr.io/manak-hash/linkbreeze منتشر شده است. برای اجرای آن به یک VPS، یک دامنه با رکورد A که به آن VPS اشاره می‌کند، باز بودن پورت‌های 80 و 443، و Docker Engine به همراه پلاگین Compose نیاز دارید.

این راهنما استقراری را پوشش می‌دهد که مخزن پروژه به‌طور رسمی از آن پشتیبانی می‌کند: Docker Compose پشت یک reverse proxy که گواهی‌های خود را دریافت می‌کند. همچنین به بررسی موارد خرابی می‌پردازد، زیرا لینک موجود در bio یک URL عمومی است که دیگران روی آن کلیک می‌کنند و خرابی آن به معنای از دست رفتن کلیک است.

پیش از هر اقدامی، توجه داشته باشید که این پروژه تا چه حد جدید است.

آیا LinkBreeze برای استفاده در لینک پروفایل عمومی به اندازه کافی بالغ است؟

تا اوت 2026، این مخزن دارای 178 ستاره، 17 فورک و تنها یک نگهدارنده است. اولین نسخه تگ‌شده، یعنی v1.0.0، مربوط به تاریخ 1 ژوئیه 2026 است. این پروژه تنها چند هفته قدمت دارد، نه چند سال.

ChartLinkBreeze tagged releases per week, v1.0.0 to v1.2.7
The data behind this chart
[
  {
    "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": 3,
    "cumulative": 17
  }
]

از زمان انتشار v1.0.0، این پروژه 17 نسخه تگ‌شده را در طول 7 هفته تقویمی عرضه کرده است. آخرین هفته در آن نمودار، هنگام نگارش این راهنما همچنان در جریان بود و به تنهایی شامل 3 مورد از این نسخه‌ها می‌شد.

این موضوع را به عنوان دو واقعیت مجزا در نظر بگیرید. نگهدارنده پروژه فعال است و باگ‌ها ظرف چند روز برطرف می‌شوند. با این حال، طرحواره (schema) و تنظیمات پیش‌فرض همچنان در حال تغییر هستند؛ بنابراین نمونه‌ای که مستقر می‌کنید و آن را به حال خود رها می‌کنید، با گذشت زمان فاصله زیادی با کدی که در حال توسعه است پیدا خواهد کرد.

مجوز (license) شما را در بدترین شرایط محافظت می‌کند. استفاده از مجوز MIT به همراه یک image کانتینر و یک فایل SQLite روی دیسک شخصی‌تان به این معناست که اگر توسعه پروژه متوقف شود، آنچه در اختیار دارید همچنان به کار خود ادامه خواهد داد. آنچه در برابر آن محافظت نمی‌شوید، یک وب‌اپلیکیشن عمومی است که دریافت اصلاحیه‌های امنیتی را متوقف می‌کند و به مرور زمان به یک نقطه ضعف امنیتی تبدیل می‌شود. این سرویس را به عنوان پروژه‌ای مستقر کنید که قصد دارید آن را به‌روز نگه دارید و روال پشتیبان‌گیری که در ادامه آمده است را از همان روز اول فعال کنید.

تگ ایمیج را ثابت نگه دارید و از latest استفاده نکنید

گردش‌کار انتشار (release workflow) دقیقاً دو تگ برای هر نسخه ارسال می‌کند: latest، و شماره نسخه که کاراکتر v از ابتدای آن حذف شده است. بنابراین تگ ثابت برای نسخه v1.2.7 برابر با ghcr.io/manak-hash/linkbreeze:1.2.7 است. نوشتن :v1.2.7 هیچ چیزی را دریافت نمی‌کند و Docker خطای manifest unknown را گزارش می‌دهد، زیرا آن تگ هرگز ارسال (push) نشده است.

آن را ثابت نگه دارید زیرا latest تغییر می‌کند. با توجه به زمان‌بندی در نمودار بالا، یک docker compose pull در برابر latest به معنای ارتقای بررسی‌نشدهٔ صفحه‌ای است که مخاطبان شما از آن استفاده می‌کنند. با استفاده از یک تگ ثابت، ارتقا زمانی رخ می‌دهد که شما فایل را ویرایش کنید.

یک نکته دیگر درباره ایمیج. گردش‌کار انتشار بدون تنظیم platforms: ساخته می‌شود، بنابراین ایمیج منتشرشده فقط linux/amd64 است. روی یک میزبان arm64، عملیات pull با خطای no matching manifest for linux/arm64/v8 in the manifest list entries شکست می‌خورد. اگر از یک VPS با معماری ARM به جای x86 استفاده می‌کنید، ایمیج را روی همان دستگاه بسازید:

git clone --branch v1.2.7 --depth 1 https://github.com/Manak-hash/LinkBreeze.git
cd LinkBreeze
docker build -t linkbreeze:1.2.7 .

سپس از linkbreeze:1.2.7 به عنوان نام ایمیج در فایل compose زیر استفاده کنید.

استقرار 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 .env

SECRET_KEY کوکی نشست مدیریت را امضا کرده و به هش بازدیدکنندگان در بخش تحلیل (analytics) نمک (salt) اضافه می‌کند. فایل compose موجود در مخزن به‌صورت پیش‌فرض از ${SECRET_KEY:-changeme-in-production} استفاده می‌کند، بنابراین اگر این مرحله را نادیده بگیرید، نمونهٔ شما با کلید امضای نشستی اجرا می‌شود که در GitHub به‌صورت عمومی در دسترس است. آن را پیش از اولین اجرا تنظیم کنید، زیرا تغییر آن در آینده باعث خروج شما از حساب کاربری و بازنشانی نمک تحلیل‌ها می‌شود.

فایل docker-compose.yml را بنویسید:

services:
  linkbreeze:
    image: ghcr.io/manak-hash/linkbreeze:1.2.7
    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 caddy

docker 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

ابتدا متوقف کنید، زیرا کپی کردن دیتابیس SQLite در حالی که فرآیندی در حال نوشتن روی آن است، ممکن است تراکنش‌های نیمه‌تمام را ثبت کند و فایل کپی‌شده به صورت خراب باز شود. در حین اجرای کپی، صفحه در دسترس نخواهد بود. بازیابی نیز همان حرکت به صورت معکوس است:

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 روی فایل‌سیستم‌های شبکه‌ای غیرقابل‌اعتماد است و خرابی صفحه، نتیجه‌ای است که با آن مواجه خواهید شد. و اگر 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 migrations) به‌طور خودکار هنگام شروع کانتینر اجرا می‌شوند. هیچ روش مستندی برای بازگرداندن (rollback) این تغییرات وجود ندارد، بنابراین ابتدا از volume کپی تهیه کنید. ارتقایی که امکان بازگشت ندارد، تنها زمانی ایمن است که بتوانید وضعیت پیش از آن را بازیابی کنید.

اگر نسخه جدیدتری از نرم‌افزار منتشر شده باشد، داشبورد یک بنر نمایش می‌دهد. این بررسی از طریق دریافت یک فایل کوچک حاوی شماره نسخه از مخزن GitHub پروژه و هر 24 ساعت یک‌بار انجام می‌شود و هیچ اطلاعاتی از نمونه (instance) شما ارسال نمی‌گردد. پیش از تغییر تگ (tag)، یادداشت‌های انتشار (release notes) را مطالعه کنید؛ زیرا در این مرحله از توسعه پروژه، یک نسخه minor ممکن است تنظیمات پیش‌فرضی که به آن‌ها وابسته‌اید را تغییر دهد.

حالت‌های شکست و پیام‌هایی که مشاهده خواهید کرد

manifest unknown هنگام pull کردن. تگ به صورت :v1.2.7 نوشته شده است. تگ‌های رجیستری هیچ v ندارند، بنابراین از :1.2.7 استفاده کنید.

no matching manifest for linux/arm64/v8 in the manifest list entries. ایمیج منتشرشده فقط برای معماری amd64 است. آن را روی میزبان ARM از سورس تگ‌شده بسازید.

کانتینر unhealthy گزارش می‌دهد در حالی که صفحه به‌درستی بارگذاری می‌شود. یک healthcheck در فایل compose شما در حال فراخوانی curl است که در ایمیج وجود ندارد. آن را حذف کنید و اجازه دهید healthcheck داخلی خودِ ایمیج یعنی wget اجرا شود.

Caddy خطای گواهی می‌دهد یا اصلاً چیزی نمایش نمی‌دهد. docker compose logs caddy را بررسی کنید. دلایل معمول این است که رکورد A هنوز به این VPS اشاره نمی‌کند، یا پورت 80 روی فایروال بسته است که باعث مسدود شدن چالش HTTP مربوط به ACME (محیط مدیریت خودکار گواهی) می‌شود؛ چالشی که Caddy برای اثبات کنترل دامنه از آن استفاده می‌کند.

تعداد بازدیدکنندگان یکتا روی 1 ثابت مانده است. هیچ پروکسی در حال تنظیم X-Forwarded-For نیست، بنابراین هشِ تمام بازدیدکنندگان یکسان محاسبه می‌شود.

کانتینر بلافاصله پس از شروع متوقف می‌شود، در حالی که دیروز کار می‌کرد. اگر از یک volume نام‌گذاری‌شده به یک host bind mount مهاجرت کرده‌اید، دایرکتوری داده متعلق به root است و برنامه با uid 1000 اجرا می‌شود، بنابراین نمی‌تواند فایل دیتابیس را باز کند. دایرکتوری میزبان را sudo chown -R 1000:1000 کنید.

درخواست‌های ردیابی با HTTP 429 پاسخ داده می‌شوند. محدودیت نرخ (throttle) به ازای هر IP روی /api/track و /go/<id> فعال شده است. بازدیدکنندگان همچنان به مقصد خود هدایت می‌شوند، فقط کلیک آن‌ها ثبت نمی‌شود.

FAQ

این یک پروژه نوپا است. تا اوت 2026، این مخزن دارای 178 ستاره، 17 فورک و یک نگهدارنده است و اولین نسخه آن در تاریخ 1 ژوئیه 2026 منتشر شده است. نسخه‌ها به‌طور میانگین بیش از دو بار در هفته منتشر می‌شوند، بنابراین باگ‌ها به‌سرعت رفع شده و رفتار برنامه نیز به‌سرعت تغییر می‌کند. مجوز MIT و فایل محلی SQLite به این معناست که حتی در صورت توقف توسعه، همچنان یک صفحه فعال خواهید داشت؛ اما یک وب‌اپلیکیشن عمومی بدون وصله‌های امنیتی به یک نقطه ضعف تبدیل می‌شود، بنابراین با آن به عنوان نرم‌افزاری برخورد کنید که باید به‌طور مداوم به‌روزرسانی شود، نه نرم‌افزاری که یک‌بار نصب و رها شود.

از کدام تگ ایمیج LinkBreeze باید استفاده کنم؟

از تگ نسخه استفاده کنید، برای مثال ghcr.io/manak-hash/linkbreeze:1.2.7، و آن را آگاهانه تغییر دهید. گردش کار انتشار فقط latest و شماره نسخه خام را ارسال می‌کند، بنابراین :v1.2.7 با v وجود ندارد و Docker پاسخ manifest unknown را برمی‌گرداند. این ایمیج فقط برای linux/amd64 ساخته شده است، بنابراین روی یک VPS با معماری arm64 باید تگ را clone کرده و به‌صورت محلی 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 از داشبورد، یک اسنپ‌شات پیکربندی از پروفایل، لینک‌ها، تنظیمات و تم‌ها است و شامل هیچ‌گونه داده تحلیلی یا تصویری نیست.

آیا وارد کردن اطلاعات از Linktree، تحلیل‌ها و تم من را نیز منتقل می‌کند؟

خیر. ابزار مهاجرت، عناوین لینک‌ها، URLها، توضیحات و تصاویر را از پروفایل عمومی قدیمی شما، به همراه نام نمایشی، بیوگرافی و آواتار می‌خواند. تاریخچه تحلیل‌ها، تم، مشترکین ایمیلی و تاریخ‌های انتشار زمان‌بندی‌شده باقی می‌مانند. پس از وارد کردن اطلاعات، ظاهر صفحه را در ویرایشگر تم بازسازی کنید و انتظار داشته باشید که تاریخچه کلیک‌های شما در پلتفرم قدیمی باقی بماند.

#linkbreeze#linktree-alternative#docker-compose#sqlite#self-hosting#analytics