> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# لوحة Homepage: كل خدماتك في صفحة واحدة
- URL: https://arabroot.io/articles/لوحة-homepage-لخدماتك/
- Published: 2026-09-25T19:10:00.000Z
- Updated: 2026-10-04T20:05:51.000Z
- Description: صفحة واحدة تجمع روابط كل خدماتك وتعرض حالتها، بدلاً من البحث عن الروابط كل مرة. نثبت لوحة Homepage وننظمها بملفات بسيطة، ونربطها بـ Docker لعرض حالة الـ Containers بأمان.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, تطبيقات لفريقك, هوم لاب, الاستضافة الذاتية, Homepage

لنفرض أن لديك سيرفراً Server تعمل عليه خمس خدمات Services أو ست، فسوف تجد نفسك بعد فترة قصيرة تسأل أسئلة صغيرة ولكنها مزعجة: أين رابط لوحة المراقبة Monitoring؟ وما هو النطاق الفرعي Subdomain الخاص بقاعدة المعرفة Knowledge Base؟ وهل ال Container الخاص بـ Gitea يعمل الآن أصلاً؟ والحل الأول الذي يخطر على البال هو أن تحفظ هذه الروابط في المفضلة Bookmarks داخل المتصفح Browser، وهذا الحل يعمل في اليوم الأول، ولكنه يفشل بسرعة لأن المفضلة تبقى على جهازك وحدك فلا يراها زميلك، ولا تخبرك هل الخدمة تعمل أم متوقفة، فتضغط على الرابط ثم تكتشف أن ال Container متوقف منذ يومين.

لذلك فالحل الأنسب هو لوحة بداية Dashboard تجمع روابط خدماتك في صفحة واحدة يفتحها كل الفريق، وتعرض بجانب كل رابط حالته. ومن أشهر اللوحات مفتوحة المصدر Open Source [Homarr](https://homarr.dev/?ref=arabroot.io) و[Dashy](https://dashy.to/?ref=arabroot.io) و[**Homepage**](https://gethomepage.dev/?ref=arabroot.io)، وسوف نشرح في هذا الدليل Homepage، وهي صفحة سريعة تعرض بجانب كل خدمة حالتها Status واستهلاكها للموارد Resources، وتضيف في أعلاها أدوات صغيرة Widgets لموارد السيرفر والبحث والوقت، وتتكامل مع أكثر من مئة تطبيق عبر واجهاتها البرمجية APIs.

وقد يتساءل البعض: لماذا Homepage وليس Homarr أو Dashy؟ والإجابة أن إعداد Homepage كله **ملفات YAML**، ولا توجد فيها واجهة لتحرير اللوحة UI Editor، وهذا يعني أنك تحفظ هذه الملفات في Git وتراجعها وتنسخها نسخاً احتياطياً Backup كأي نص، وهو ما يناسب من يدير سيرفره بنفسه ويفضل أن يكون الإعداد كوداً Configuration as Code. أما إذا أردت لوحة يعدلها زملاء غير تقنيين بالسحب والإفلات Drag and Drop، فإن Homarr أقرب إلى حاجتك.

**وتذكر** أن Homepage تعمل دون تسجيل دخول Authentication في إعدادها الافتراضي، فكل من يصل إلى رابطها يرى خدماتك، لذلك فحمايتها مسؤوليتك كما سوف نشرح لاحقاً.

وسوف نناقش في هذا المقال ما يلي:

- تثبيت Homepage بملف Compose مع Socket Proxy للقراءة فقط، ولماذا لا نركب `/var/run/docker.sock` داخلها مباشرة.
- ملفات الإعداد الخمسة، ثم الاكتشاف التلقائي للخدمات عبر وسوم Docker، وأدوات الخدمات مع حماية مفاتيحها.
- الوصول عبر نطاق وحماية اللوحة، لأنها لا تطلب تسجيل دخول في إعدادها الافتراضي.
- التحقق من النجاح والنسخ الاحتياطي والتحديث وأشهر المشكلات وحلولها.

## ما تحتاجه قبل أن تبدأ Requirements

- سيرفر عليه Docker وCompose، وإذا لم يكن مثبتاً فابدأ بدليل [تثبيت Docker على Ubuntu](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-docker-%D8%B9%D9%84%D9%89-ubuntu/).
- الموارد: تستهلك Homepage قرابة 120 ميجابايت من الذاكرة RAM، والـ Socket Proxy قرابة 25 ميجابايت، وهذا يعني أنها تعمل بجانب خدماتك دون أن تشعر بها.
- نطاق فرعي مثل `home.example.com`، وReverse Proxy مثل [Nginx Proxy Manager](https://arabroot.io/articles/nginx-proxy-manager-%D9%86%D8%B7%D8%A7%D9%82%D8%A7%D8%AA-%D9%88%D8%B4%D9%87%D8%A7%D8%AF%D8%A7%D8%AA-tls/).
- لا تحتاج إلى فتح أي منفذ Port على الإنترنت، والسبب أن الوصول كله يمر عبر ال Reverse Proxy.

## تثبيت Homepage Installation

### لماذا نضع Socket Proxy بدلاً من تركيب ال socket مباشرة؟

لكي تعرض Homepage حالة ال Containers فهي تحتاج إلى أن تسأل Docker عنها Query، والطريقة الأبسط التي تجدها في أغلب الأمثلة هي أن تركب Mount الملف `/var/run/docker.sock` داخلها، والمشكلة في هذه الطريقة أن من يصل إلى هذا الملف يستطيع أن يطلب من Docker إنشاء Container بصلاحيات Permissions حساب root على السيرفر، كما يشرح [توثيق أمان Docker Security](https://docs.docker.com/engine/security/?ref=arabroot.io#docker-daemon-attack-surface)، وبالتالي تصبح أي ثغرة في اللوحة باباً إلى السيرفر كله. وقد تظن أن العلامة `:ro` تحل المشكلة، ولكنها لا تغير شيئاً هنا، والسبب أنها تمنع تعديل الملف نفسه ولا تمنع إرسال الأوامر عبره، بالإضافة إلى أن Homepage تحتاج عندها إلى العمل بـ root حتى تقرأ ال socket.

لذلك فالحل الأنسب أن نضع بينهما [**docker-socket-proxy**](https://github.com/Tecnativa/docker-socket-proxy?ref=arabroot.io)، وهو Container صغير يركب ال socket ويقدم للشبكة الداخلية Internal Network واجهة Docker محدودة الصلاحيات، حيث نسمح فيه بقراءة ال Containers فقط (`CONTAINERS=1`) ونمنع كل طلبات الكتابة Write Requests (`POST=0`)، وهما الإعدادان اللذان يكفيان Homepage، أما `SERVICES` و`TASKS` التي تراها في أمثلة التوثيق فلا تحتاج إليها إلا في Docker Swarm. وبما أن Homepage لا تتصل بال socket مباشرة، فنستطيع [تشغيلها بمستخدم عادي Non-root User](https://gethomepage.dev/installation/docker/?ref=arabroot.io#running-as-non-root) وليس بـ root.

### ملف Compose

الآن سوف نقوم بإنشاء مجلد الإعداد ونجعله ملكاً للمستخدم 1000:

```bash
sudo mkdir -p /opt/homepage/config
sudo chown -R 1000:1000 /opt/homepage/config
cd /opt/homepage
```

ثم أنشئ الملف `/opt/homepage/compose.yaml` كما يلي، حيث إن الشبكة `proxy` هي الشبكة الخارجية External التي يعمل عليها ال Reverse Proxy، وإذا لم تكن موجودة فأنشئها بالأمر `docker network create proxy`، أما الشبكة `internal` فهي شبكة [داخلية](https://docs.docker.com/reference/compose-file/networks/?ref=arabroot.io#internal) بين Homepage وال Socket Proxy فقط ولا تتصل بالإنترنت:

```yaml
services:
  dockerproxy:
    image: tecnativa/docker-socket-proxy:v0.5.0
    container_name: homepage-dockerproxy
    restart: unless-stopped
    environment:
      CONTAINERS: 1
      POST: 0
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - internal

  homepage:
    image: ghcr.io/gethomepage/homepage:v2.4.0
    container_name: homepage
    restart: unless-stopped
    environment:
      HOMEPAGE_ALLOWED_HOSTS: home.example.com
      PUID: 1000
      PGID: 1000
    volumes:
      - ./config:/app/config
    networks:
      - internal
      - proxy
    depends_on:
      - dockerproxy

networks:
  internal:
    internal: true
  proxy:
    external: true
```

في الإعداد أعلاه لاحظ التالي:

- المتغير [HOMEPAGE\_ALLOWED\_HOSTS](https://gethomepage.dev/installation/?ref=arabroot.io#homepage%5Fallowed%5Fhosts) **إلزامي** منذ الإصدار 1.0 حتى تفتح اللوحة باسم غير localhost، وتكتب فيه أسماء المضيف Hostnames التي تفتح بها اللوحة مفصولة بفواصل دون مسافات، مع المنفذ إن وجد، وأي اسم آخر ترفضه Homepage بالرمز `400`، والسبب أن هذا الفحص يحمي واجهتها الداخلية من هجمات DNS rebinding. ولاحظ أن `localhost:3000` و`127.0.0.1:3000` مسموحان دائماً، لذلك لا تحتاج إلى إضافتهما للاختبار المحلي Local Testing.
- المتغيران `PUID` و`PGID` يشغلان العملية Process بالمستخدم 1000 وليس بـ root، ولهذا جعلنا مجلد الإعداد ملكاً لهذا المستخدم، ولاحظ أن Homepage تصحح ملكية المجلد بنفسها عند التشغيل وتكتب ذلك في السجل بالعبارة `Fixing ownership of /app/config`.
- ثبتنا الوسم Tag بالرقم الكامل بدلاً من `latest` الذي تستخدمه أمثلة التوثيق، والسبب أن الإصدار لن يتغير دون علمك عند أول `docker compose pull`.
- لم ننشر أي منفذ لا لـ Homepage ولا لل Socket Proxy، فالوصول إلى اللوحة عبر ال Reverse Proxy فقط، وال Socket Proxy لا يراه أحد غير Homepage على الشبكة `internal`. وفي ال Image فحص صحة Healthcheck جاهز على المسار `/api/healthcheck`.

بعد ذلك شغل الخدمة وراجع السجل Log:

```bash
docker compose up -d
docker compose ps
docker compose logs --tail 20 homepage
```

والمخرج سوف يكون كما يلي:

```bash
NAME                   STATUS
homepage               Up 30 seconds (healthy)
homepage-dockerproxy   Up 31 seconds
...
✓ Ready in 0ms
[info]: settings.yaml was copied to the config folder
```

وفي التشغيل الأول تنسخ Homepage ملفات إعداد نموذجية إلى `config/`، وسوف نضع ملفاتنا مكانها في القسم التالي.

## ملفات الإعداد Configuration Files

كل ملف مسؤول عن جزء من الصفحة، وتقرأ Homepage الملفات من جديد كلما حدثت الصفحة، وبالتالي لا تحتاج إلى إعادة تشغيل Restart ال Container بعد كل تعديل. والشكل التالي يبين النتيجة التي سوف نصل إليها:

![لوحة Homepage بسمة داكنة تعرض موارد الخادم والبحث والتاريخ ومجموعتي خدمات وإشارات مرجعية](https://arabroot.io/content/images/2026/09/homepage-01-dashboard.webp)

اللوحة بعد الإعداد: أدوات المعلومات Info Widgets في الأعلى، ثم مجموعات الخدمات Groups، ثم الإشارات المرجعية Bookmarks

### settings.yaml: العنوان والسمة وترتيب المجموعات

نبدأ بالملف الذي يحدد عنوان الصفحة Title وسمتها Theme وتخطيطها Layout:

```yaml
title: بوابة الفريق
theme: dark
color: slate
headerStyle: boxed
statusStyle: dot
layout:
  Applications:
    style: row
    columns: 3
  Infrastructure:
    style: row
    columns: 3
```

في الإعداد أعلاه لاحظ أن القسم [layout](https://gethomepage.dev/configs/settings/?ref=arabroot.io#layout) يحدد ترتيب المجموعات وشكلها، والأسماء فيه يجب أن تطابق أسماء المجموعات في `services.yaml` حرفاً بحرف.

### docker.yaml: الاتصال بـ Docker

في هذا الملف نعرف خادم Docker باسم نستخدمه في بقية الملفات، ونوجهه إلى ال Socket Proxy باسم ال Container الخاص به على الشبكة الداخلية، كما في [توثيق إعداد Docker](https://gethomepage.dev/configs/docker/?ref=arabroot.io#using-docker-socket-proxy):

```yaml
my-docker:
  host: dockerproxy
  port: 2375
```

وإذا فضلت تركيب ال socket مباشرة في ال Container الخاص بـ Homepage، وقبلت المخاطرة التي شرحناها وتشغيلها بـ root، فاكتب [socket: /var/run/docker.sock](https://gethomepage.dev/configs/docker/?ref=arabroot.io#using-socket-directly) مكان `host` و`port`، ولكننا لا ننصح بذلك. ولإضافة سيرفر ثان، أضف تعريفاً آخر يشير إلى Socket Proxy على ذلك السيرفر عبر شبكة خاصة Private Network، ولا تنشر المنفذ 2375 على عنوان عام، والسبب أن أي منفذ يجيب عن Docker API هو نافذة على كل ما يعمل في السيرفر.

### services.yaml: الخدمات

[هذا الملف](https://gethomepage.dev/configs/services/?ref=arabroot.io) مقسم إلى مجموعات، وفي كل مجموعة عدد من الخدمات، ولكل خدمة اسم ورابط ووصف Description وأيقونة Icon، وتستطيع ربطها بـ Container حتى تعرض حالته:

```yaml
- Applications:
    - Git:
        href: https://git.example.com
        description: مستودعات الشيفرة
        icon: gitea.svg
    - Wiki:
        href: https://wiki.example.com
        description: قاعدة معرفة الفريق
        icon: outline.svg

- Infrastructure:
    - Portainer:
        href: https://portainer.example.com
        description: إدارة الحاويات
        icon: portainer.svg
    - Nginx Proxy Manager:
        href: https://npm.example.com
        description: النطاقات والشهادات
        icon: nginx-proxy-manager.svg
    - Homepage:
        href: https://home.example.com
        description: هذه اللوحة
        icon: homepage.png
        server: my-docker
        container: homepage
```

في الإعداد أعلاه لاحظ التالي:

- **الأيقونات:** إذا كتبت اسم ملف مثل `gitea.svg` فسوف تجلبه Homepage من مجموعة [dashboard-icons](https://github.com/homarr-labs/dashboard-icons?ref=arabroot.io)، وهي [تقبل](https://gethomepage.dev/configs/services/?ref=arabroot.io#icons) أيضاً البادئة Prefix `mdi-` لأيقونات [Material Design](https://pictogrammers.com/library/mdi/?ref=arabroot.io)، والبادئة `si-` لأيقونات [Simple Icons](https://simpleicons.org/?ref=arabroot.io) (مثل `si-github`)، أو رابطاً كاملاً لملف أيقونة. وانتبه إلى الامتداد Extension، والسبب أن بعض الأيقونات متوفر بصيغة `png` فقط، ومنها أيقونة Homepage نفسها.
- **server وcontainer:** هذان الحقلان يربطان البطاقة Card بـ Container، فتظهر [شارة الحالة Badge](https://gethomepage.dev/configs/services/?ref=arabroot.io#docker-integration) (`RUNNING`، أو `HEALTHY` إذا كان لل Container فحص صحة)، ومعها زر يعرض استهلاك المعالج CPU والذاكرة والشبكة.

![بطاقة خدمة في Homepage مع شارة HEALTHY وإحصاءات CPU وMEM وRX وTX](https://arabroot.io/content/images/2026/09/homepage-02-container-stats.webp)

إحصاءات ال Container المرتبط بالبطاقة، ومصدرها Docker عبر ال Socket Proxy

### الاكتشاف التلقائي للخدمات Auto Discovery بوسوم Docker

كتابة كل خدمة في `services.yaml` تعمل جيداً عندما تكون الخدمات قليلة، ولكنك سوف تنسى مع الوقت أن تحذف خدمة أزلتها أو تضيف خدمة جديدة، فتصبح اللوحة غير مطابقة لما يعمل فعلاً. لذلك تستطيع الخدمة أن [تعلن عن نفسها](https://gethomepage.dev/configs/docker/?ref=arabroot.io#automatic-service-discovery) بوسوم Labels في ملف Compose الخاص بها، فتظهر في اللوحة ما دامت تعمل وتختفي عندما تحذفها. لنأخذ مثالاً بالتطبيق `whoami`، حيث أضفنا إليه الخيار [siteMonitor](https://gethomepage.dev/configs/services/?ref=arabroot.io#site-monitor) الذي يطلب الرابط بشكل دوري ويعرض زمن الاستجابة Latency:

```yaml
services:
  whoami:
    image: traefik/whoami:v1.11.0
    container_name: whoami
    restart: unless-stopped
    labels:
      homepage.group: Applications
      homepage.name: Whoami
      homepage.icon: traefik.svg
      homepage.href: https://whoami.example.com
      homepage.description: تطبيق اختبار الوكيل
      homepage.server: my-docker
      homepage.container: whoami
      homepage.siteMonitor: http://whoami:80
    networks:
      - homepage_internal

networks:
  homepage_internal:
    external: true
```

في الإعداد أعلاه لاحظ التالي:

- الوسمان `homepage.server` و`homepage.container` اختياريان هنا، والسبب أن Homepage تستنتجهما تلقائياً من ال Container الذي يحمل الوسوم، ولكننا كتبناهما للوضوح.
- لكي يصل `siteMonitor` إلى ال Container باسمه، يجب أن يكون ال Container على شبكة تصل إليها Homepage، واسم الشبكة الداخلية من ملفنا يصبح `homepage_internal`، لأن Compose [يضيف اسم المشروع Project Name](https://docs.docker.com/compose/how-tos/networking/?ref=arabroot.io) في أوله. وإذا كان الرابط العام يعمل، فيكفيك `siteMonitor: https://whoami.example.com` دون مشاركة الشبكة.
- في لقطة الشاشة Screenshot الأولى سوف ترى بطاقة Whoami وعليها `3 MS` و`RUNNING`.

### widgets.yaml: أدوات المعلومات

هذا الملف يحدد [الشريط العلوي](https://gethomepage.dev/configs/info-widgets/?ref=arabroot.io) في الصفحة، وفيه موارد السيرفر وصندوق البحث Search Box والتاريخ والوقت:

```yaml
- resources:
    cpu: true
    memory: true
    disk: /
- search:
    provider: duckduckgo
    target: _blank
- datetime:
    text_size: xl
    format:
      timeStyle: short
      dateStyle: long
```

ولاحظ أن أداة [resources](https://gethomepage.dev/widgets/info/resources/?ref=arabroot.io) تقيس الموارد كما يراها ال Container، فالقرص `/` هنا هو نظام الملفات Filesystem الخاص بال Container، وغالباً هو قرص Docker على السيرفر. ولكي تقيس قرص بيانات آخر، قم بتركيبه في ال Container للقراءة فقط Read-only (مثل `/srv:/srv:ro`)، ثم اكتب `disk: /srv`.

### bookmarks.yaml: الإشارات المرجعية

وهنا تضع الروابط التي كنت تحفظها في مفضلة المتصفح، ولكنها الآن في مكان يراه كل الفريق:

```yaml
- Docs:
    - Docker:
        - abbr: DK
          href: https://docs.docker.com/
    - Ubuntu Server:
        - abbr: UB
          href: https://ubuntu.com/server/docs
- Community:
    - ArabRoot:
        - abbr: AR
          href: https://example.com/
    - GitHub:
        - icon: github.svg
          href: https://github.com/
```

و[كل إشارة](https://gethomepage.dev/configs/bookmarks/?ref=arabroot.io) تحمل اختصاراً Abbreviation من حرفين (`abbr`) أو أيقونة.

### أدوات الخدمات Service Widgets والأسرار Secrets

لكل تطبيق مدعوم أداة تعرض أرقامه داخل البطاقة، مثل عدد ال Containers في Portainer أو عدد المضيفين في Nginx Proxy Manager، وتحتاج الأداة عادة إلى رابط داخلي ومفتاح API. ولا تكتب المفاتيح في YAML مباشرة، والسبب أن ملفات YAML تذهب إلى Git وإلى النسخ الاحتياطية، وإنما [مررها كمتغيرات بيئة Environment Variables](https://gethomepage.dev/installation/docker/?ref=arabroot.io#using-environment-secrets) تبدأ بـ `HOMEPAGE_VAR_`، ثم أشر إليها بين قوسين مزدوجين. والمثال التالي من [التوثيق الرسمي لأداة Portainer](https://gethomepage.dev/widgets/services/portainer/?ref=arabroot.io):

```yaml
    - Portainer:
        href: https://portainer.example.com
        icon: portainer.svg
        widget:
          type: portainer
          url: https://portainer:9443
          env: 1
          key: "{{HOMEPAGE_VAR_PORTAINER_KEY}}"
```

بعد ذلك أضف في `compose.yaml` السطر `HOMEPAGE_VAR_PORTAINER_KEY: ${PORTAINER_KEY}` تحت `environment`، وضع القيمة في ملف `.env` بالصلاحية `600`، وأنشئ للأداة مستخدماً أو مفتاحاً بصلاحية قراءة فقط، والسبب أن هذا المفتاح سوف يكون محفوظاً في اللوحة، فإذا تسرب فلن يستطيع من يملكه أن يغير شيئاً.

وتتكيف اللوحة مع شاشة الهاتف Mobile تلقائياً، فتصطف المجموعات عمودياً كما في الشكل التالي:

![لوحة Homepage على شاشة بعرض هاتف](https://arabroot.io/content/images/2026/09/homepage-03-mobile.webp)

العرض على الهاتف

## الوصول عبر نطاق Domain وحماية اللوحة

أنشئ في Nginx Proxy Manager مضيفاً Proxy Host للنطاق `home.example.com` يوجه الطلبات Requests إلى `http://homepage:3000`، والسبب أن ال Containers الاثنين على الشبكة `proxy`، ثم اطلب شهادة Certificate من Let's Encrypt وفعل Force SSL. بعد ذلك تأكد أن الاسم نفسه موجود في `HOMEPAGE_ALLOWED_HOSTS`، وإلا ظهرت لك صفحة فارغة أو الرمز `400`.

🛑

لا تطلب Homepage تسجيل دخول في إعدادها الافتراضي، لذلك يرى كل من يفتح الرابط أسماء خدماتك وروابطها الداخلية وحالة ال Containers لديك وأي أرقام تعرضها أدوات الخدمات، فلا تتحها للعامة دون حماية، وإنما قيدها بقائمة وصول Access List في ال Reverse Proxy (عناوين المكتب أو كلمة مرور Password)، أو اجعل الوصول إليها عبر شبكة خاصة افتراضية VPN كما في دليل [شبكة خاصة مع WireGuard وwg-easy](https://arabroot.io/articles/%D8%B4%D8%A8%D9%83%D8%A9-%D8%AE%D8%A7%D8%B5%D8%A9-%D9%85%D8%B9-wireguard-%D9%88-wg-easy/)، أو ضعها خلف دخول موحد SSO كما في دليل [حماية أي تطبيق باستخدام Authentik Forward Auth](https://arabroot.io/articles/%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D8%A3%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-authentik-forward-auth/).

وقد تسأل: ألا توجد في Homepage حماية خاصة بها؟ والإجابة أن الإصدار 2.0 أضاف [بوابة دخول بسيطة](https://gethomepage.dev/installation/?ref=arabroot.io#security-authentication) بكلمة مرور أو عبر OIDC، ولكنها معطلة حتى تضيف المتغير `HOMEPAGE_AUTH_ENABLED=true` ومعه `HOMEPAGE_AUTH_SECRET` و`HOMEPAGE_EXTERNAL_URL` وكلمة المرور في `HOMEPAGE_AUTH_PASSWORD`. ويجدر الإشارة هنا أن التوثيق نفسه ما زال يوصي بوضع اللوحة خلف Reverse Proxy يفرض الدخول أو خلف VPN، والسبب أن هذه البوابة لا تحد من عدد محاولات كلمة المرور Rate Limiting، لذلك اعتبرها طبقة إضافية وليست بديلاً عن Forward Auth أو VPN.

📌

في يونيو 2024 نشر فريق Homepage التحذير الأمني [GHSA-24m5-7vjx-9x37](https://github.com/gethomepage/homepage/security/advisories/GHSA-24m5-7vjx-9x37?ref=arabroot.io) بدرجة خطورة حرجة Critical، حيث تبين أن عدة تكاملات Integrations في الإصدارات قبل 0.9.1 تسمح بأن تطلب من الخدمات المرتبطة باللوحة واجهات API غير التي صممت لها الأداة وتقرأ ردودها، وهذا قد يكشف مفاتيح API وكلمات المرور والإعدادات الداخلية، بل قد يصل إلى تنفيذ الأوامر عن بعد Remote Code Execution. والدرس هنا أن اللوحة التي تحمل مفاتيح خدماتك هي نفسها هدف للمخترق، فلا تتركها مكشوفة للعامة، وقم بتحديثها عند صدور أي تحذير أمني.

## كيف تتأكد أن كل شيء يعمل؟ Verification

- الأمر `docker compose ps` يعرض ال Container `homepage` بالحالة `healthy`.
- اللوحة تفتح على `https://home.example.com`، والطلب باسم غير مسموح يعود بالرمز `400`، ويمكنك اختبار ذلك من Container على الشبكة نفسها بالأمر `curl -s -o /dev/null -w '%{http_code}' -H 'Host: other.example.net' http://homepage:3000/`.
- البطاقات المرتبطة بـ Containers تظهر بالحالة `RUNNING` أو `HEALTHY`، وإذا أوقفت Container للتجربة فسوف ترى الشارة تتغير.
- العملية تعمل بمستخدم غير root، حيث يعرض الأمر `docker compose exec homepage ps -o user,comm` المستخدم `node` أمام `next-server`.
- ال Socket Proxy يرفض طلبات الكتابة، ويمكنك أن تتأكد من ذلك من داخل ال Container الخاص بـ Homepage بطلب إيقاف Container:

```bash
docker compose exec homepage wget -q -O- --post-data="" http://dockerproxy:2375/containers/whoami/stop
```

والمخرج سوف يكون كما يلي:

```bash
wget: server returned error: HTTP/1.0 403 Forbidden
```

بل حتى طلب قائمة ال Images (`/images/json`) يعود بالرمز `403`، والسبب أننا سمحنا بال Containers وحدها، وبهذا الشكل نكون قد تأكدنا أن ثغرة في اللوحة لن تمنح المخترق تحكماً في Docker.

## النسخ الاحتياطي والاستعادة Restore

كل ما تحتاجه Homepage موجود في المجلد `config/` والملف `compose.yaml`، والملف `.env` إن وجد، ولا توجد فيها قاعدة بيانات Database، لذلك لا تحتاج إلى إيقاف الخدمة قبل النسخ:

```bash
cd /opt
sudo tar czf homepage-$(date +%F).tar.gz --exclude=homepage/config/logs homepage
```

والأفضل أن تجعل `/opt/homepage` مستودع Git خاصاً Repository، وتستثني `.env` و`config/logs` في `.gitignore`، فيبقى لديك سجل بكل تعديل ومن قام به. ثم انقل الأرشيف Archive إلى خارج السيرفر، والسبب أن النسخة المحفوظة على نفس القرص لن تنفعك إذا فقدت القرص نفسه. وللاستعادة فك الأرشيف في `/opt`، وتأكد أن ملكية Ownership المجلد `config/` هي `1000:1000`، ثم نفذ `docker compose up -d`.

## التحديث Upgrade إلى إصدار أحدث

1. راجع [صفحة الإصدارات Releases](https://github.com/gethomepage/homepage/releases?ref=arabroot.io)، خاصة عندما يتغير رقم الإصدار الرئيسي Major Version، والسبب أن بعض الأدوات تغير أسماء حقولها بين الإصدارات.
2. خذ نسخة من `config/` كما في القسم السابق.
3. غير الوسم في `compose.yaml`، وغير وسم ال Socket Proxy إذا صدر له [إصدار](https://github.com/Tecnativa/docker-socket-proxy/releases/tag/v0.5.0?ref=arabroot.io) أحدث، ثم نفذ:

```bash
cd /opt/homepage
docker compose pull
docker compose up -d
docker compose logs --tail 30 homepage
```

بعد ذلك ابحث في السجل عن أخطاء في قراءة YAML أو تحذيرات عن حقول لم تعد مدعومة، وإذا ظهرت مشكلة فالرجوع إلى الإصدار السابق Rollback سهل، حيث تعيد الوسم القديم وتنفذ `docker compose up -d` من جديد.

## مشكلات شائعة وحلولها Troubleshooting

### صفحة فارغة أو الرسالة Host validation failed

السبب أن اسم المضيف الذي فتحت به اللوحة، ومعه المنفذ إن كتبته، غير موجود في `HOMEPAGE_ALLOWED_HOSTS`، وسوف تجد الاسم المرفوض في السجل بعد العبارة `Host validation failed for:`، فأضفه كما هو وأعد إنشاء ال Container بالأمر `docker compose up -d`. وتجد حالات أخرى في [صفحة استكشاف الأخطاء](https://gethomepage.dev/troubleshooting/?ref=arabroot.io).

### لماذا تعرض بطاقة ال Container خطأ أو لا تظهر حالتها؟

- قيمة `container` يجب أن تطابق **اسم ال Container** كما يظهر في `docker ps`، وليس اسم الخدمة في Compose، ولهذا نستخدم `container_name`.
- قيمة `server` يجب أن تطابق اسماً معرفاً في `docker.yaml`.
- تأكد أن Homepage وال Socket Proxy على الشبكة نفسها، حيث يجب أن يعيد الأمر `docker compose exec homepage wget -q -O- http://dockerproxy:2375/version` بيانات JSON.
- الرمز `403` في السجل يعني أن ال Socket Proxy يمنع ما تطلبه Homepage، ولاحظ أن الإعداد `CONTAINERS=1` يكفي لعرض الحالة والإحصاءات، فلا تفتح صلاحيات أخرى لحل هذه المشكلة.

### لماذا لا تظهر الخدمات المعرفة بالوسوم؟

الاكتشاف يحتاج إلى ملف `docker.yaml` معرف، وإلى Container يحمل على الأقل الوسمين `homepage.group` و`homepage.name`، ويمكنك أن تتحقق من الوسوم بالأمر `docker inspect whoami --format '{{json .Config.Labels}}'`. وتذكر أن Docker يقرأ الوسوم عند إنشاء ال Container فقط، لذلك فأي تعديل عليها يحتاج إلى `docker compose up -d` لل Container المعني.

### لا تظهر الأيقونات

السبب إما أن الاسم أو الامتداد غير موجود في مجموعة الأيقونات (مثل `homepage.svg` مكان `homepage.png`)، وإما أن متصفح المستخدم لا يصل إلى شبكة CDN التي تأتي منها الأيقونات. وفي البيئات المعزولة Air-gapped ضع الأيقونات في مجلد تركبه على `/app/public/icons` (مثل `./icons:/app/public/icons:ro`)، ثم أشر إليها بـ `/icons/اسم-الملف.svg`.

### خطأ في YAML يعطل الصفحة

مسافة زائدة واحدة أو علامة تبويب Tab واحدة تكفي لتعطيل الصفحة، لذلك افحص الملف قبل الحفظ بالأمر `docker run --rm -v "$PWD/config":/c:ro mikefarah/yq:4.53.6 '.' /c/services.yaml` (أداة [yq](https://github.com/mikefarah/yq?ref=arabroot.io))، أو بأي أداة فحص Linter لملفات YAML في المحرر Editor الذي تستخدمه، ثم راجع `docker compose logs homepage`.

## الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

- المفضلة في المتصفح لا يراها الفريق ولا تخبرك بحالة الخدمات، أما Homepage فتجمع الروابط وحالة ال Containers في صفحة واحدة، وكل إعدادها ملفات YAML تحفظها في Git.
- لا تركب `/var/run/docker.sock` داخل اللوحة، وإنما ضع بينهما Socket Proxy بالإعداد `CONTAINERS=1` و`POST=0` على شبكة داخلية، وشغل Homepage بمستخدم غير root.
- المتغير `HOMEPAGE_ALLOWED_HOSTS` إلزامي، وأي اسم غير موجود فيه يعود بالرمز `400`.
- لا تطلب Homepage تسجيل دخول في إعدادها الافتراضي، لذلك ضعها خلف Forward Auth أو VPN أو قائمة وصول، ومرر مفاتيح الأدوات عبر متغيرات `HOMEPAGE_VAR_` بصلاحية قراءة فقط.
- انسخ `config/` و`compose.yaml` و`.env` إلى خارج السيرفر، وثبت الوسم بالرقم الكامل وراجع صفحة الإصدارات قبل كل تحديث.

## سجل التحديثات (Changelog)

- سبتمبر 2026: كتابة الدليل واختباره على [Homepage v2.4.0](https://github.com/gethomepage/homepage/releases/tag/v2.4.0?ref=arabroot.io).
- أكتوبر 2026: مراجعة الدليل واختباره من جديد على الإصدار نفسه، وتصحيح ما يخص تسجيل الدخول (بوابة الدخول التي أضافها الإصدار 2.0 ومعطلة افتراضياً)، وتوضيح أن `localhost:3000` مسموح دائماً، وأن وسمي `server` و`container` اختياريان في الاكتشاف التلقائي، وإضافة رابط دليل WireGuard.