> ## 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.

# تثبيت Gitea على خادمك: خادم Git خاص لفريقك باستخدام Docker Compose وPostgreSQL
- URL: https://arabroot.io/articles/تثبيت-gitea-مع-docker-compose/
- Published: 2026-09-25T15:10:00.000Z
- Updated: 2026-10-04T20:05:43.000Z
- Description: إذا أردت أن يبقى كود فريقك على سيرفرك دون أن تخسر تجربة تشبه GitHub، فهذا الدليل يثبت لك Gitea مع PostgreSQL ويشغل SSH على منفذ لا يتعارض مع السيرفر، ثم يجهز معك النسخ الاحتياطي والاستعادة والتحديث.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, تطبيقات لفريقك, الاستضافة الذاتية, Gitea

لنفرض أن كود فريقك Source Code موجود اليوم على GitHub أو على خدمة GitLab السحابية Cloud، فسوف تجد أن شركة أخرى هي التي تقرر من يصل إلى هذا الكود وكم تدفع وما هي شروط الاستخدام، وقد تفرض عليك العقود أو الأنظمة في مؤسستك أن يبقى الكود داخلها، أو تريد ببساطة أن تخفض التكلفة، وبالتالي تحتاج إلى سيرفر Git خاص يعمل على سيرفرك Server أنت، والسؤال هنا هو: ما أخف طريقة لتشغيله دون أن تخسر ما تعود عليه فريقك؟

والحل الأول الذي يخطر على البال هو أن تنشئ على السيرفر مستودعات Git عادية من نوع Bare Repository ويصل إليها المطورون عبر SSH، وهذا الحل يعمل في اليوم الأول، ولكنه يفشل بسرعة لأن كل مطور يحتاج إلى حساب على نظام التشغيل نفسه، ولا توجد صلاحيات Permissions مختلفة لكل مستودع، ولا توجد واجهة ويب Web UI تراجع فيها الكود Code Review أو تفتح فيها طلبات الدمج Pull Requests والتذاكر Issues، فتعود الملاحظات إلى المحادثات وتضيع. والحل الثاني هو أن تثبت GitLab على سيرفرك، وهو حل كامل ولكنه ثقيل، حيث يحتاج إلى عدة جيجابايتات من الذاكرة RAM وعدة خدمات داخلية، وهذا كثير على فريق صغير وسيرفر متواضع.

لذلك فالحل الأنسب في أغلب الفرق الصغيرة والمتوسطة هو [**Gitea**](https://about.gitea.com/?ref=arabroot.io)، وهو سيرفر Git خفيف ومفتوح المصدر Open Source، يشبه GitHub في شكله وطريقة عمله، ففيه المستودعات Repositories والمنظمات Organizations والفرق Teams، وطلبات الدمج ومراجعتها، والتذاكر والمشاريع Projects والويكي Wiki، وفيه أيضاً [سجل حزم Packages](https://docs.gitea.com/usage/packages/overview/?ref=arabroot.io) يستضيف Docker Images وحزم npm وNuGet وغيرها، و[Gitea Actions](https://docs.gitea.com/usage/actions/overview/?ref=arabroot.io) المتوافقة مع GitHub Actions، وكل هذا في Container واحد يكفيه بحسب [توثيق Gitea](https://docs.gitea.com/?ref=arabroot.io) معالجان وذاكرة 1 GB لفريق صغير.

وقد يتساءل البعض: لماذا لا أبقى على GitHub وأدفع الاشتراك وأرتاح من إدارة سيرفر جديد؟ والإجابة أن Gitea يناسبك إذا كان المطلوب أن يبقى الكود على سيرفرك، أو احتجت إلى Container Registry خاص دون اشتراك مدفوع Paid Subscription، وأما إذا كان فريقك يعتمد كثيراً على ميزات خاصة بـ GitHub مثل Codespaces وCopilot وMarketplace، فاحسب كلفة الانتقال أولاً لأن هذه الميزات لن تنتقل معك.

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

- تثبيت Gitea باستخدام Docker Compose مع قاعدة بيانات PostgreSQL، وشرح كل سطر مهم في الملف.
- تشغيل SSH على منفذ غير المنفذ 22 حتى لا يتعارض مع SSH الخاص بالسيرفر، وربط النطاق مع شهادة TLS.
- إكمال معالج التثبيت، وإنشاء أول مستودع والعمل عليه عبر SSH، وإعداد البريد.
- النسخ الاحتياطي بالأمر `gitea dump` والاستعادة على سيرفر جديد، ثم التحديث وأشهر المشكلات وحلولها.

وبعد هذا الدليل تستطيع [تشغيل CI/CD بـ Gitea Actions](https://arabroot.io/articles/gitea-actions-%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-ci-cd-%D8%B9%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85%D9%83/)، و[ربط تسجيل الدخول بـ authentik](https://arabroot.io/articles/%D8%B1%D8%A8%D8%B7-gitea-%D9%85%D8%B9-authentik-%D8%B9%D8%A8%D8%B1-oidc/).

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

- سيرفر Linux بمعالجين 2 vCPU وذاكرة RAM بحجم 1 GB على الأقل، و2 GB تعطيك هامشاً مريحاً مع PostgreSQL وActions، وأما المساحة Storage فتتوقف على حجم مستودعاتك وحزمك، و20 GB تكفي للبداية.
- Docker Engine مع Compose v2، وإذا لم يكن مثبتاً فاتبع [دليل تثبيت Docker](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-docker-%D8%B9%D9%84%D9%89-ubuntu/).
- نطاق فرعي Subdomain مثل `git.example.com` يشير إلى السيرفر، وReverse Proxy يصدر شهادة TLS Certificate مثل [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 لاتصالات Git عبر SSH، ولأن خدمة SSH الخاصة بالسيرفر تستخدم المنفذ 22 عادة، فسوف نربط SSH الخاص بـ Gitea بمنفذ آخر هو `2222`، ولا تنس أن تفتحه في جدار الحماية Firewall.

## تثبيت Gitea خطوة بخطوة Installation

### مجلد التثبيت وملف .env

أول خطوة هي أن ننشئ مجلداً للتثبيت، ونضع فيه ملف `.env` يحمل كلمة مرور قاعدة البيانات Database، والسبب أننا لا نريد أن نكتب كلمة المرور داخل ملف Compose نفسه فتظهر في كل نسخة منه، ولذلك نولدها عشوائياً ونجعل الملف مقروءاً للمالك فقط:

```bash
sudo mkdir -p /opt/gitea
cd /opt/gitea
echo "GITEA_DB_PASSWORD=$(openssl rand -hex 24)" > .env
chmod 600 .env
```

### ملف docker-compose.yml وشرح ما فيه

اخترنا PostgreSQL بدل SQLite لأنها تتحمل الاستخدام المتزامن Concurrency عندما يعمل عدة مطورين في نفس الوقت، ونسخها الاحتياطي Backup سهل بأداتها الأصلية. والمتغيرات Variables التي تبدأ بـ `GITEA__section__KEY` يكتبها Gitea في ملف الإعدادات `app.ini` عند كل تشغيل، وبالتالي فهي المرجع الفعلي للإعدادات، ومنها يملأ معالج التثبيت Installation Wizard حقوله Fields تلقائياً. وتجد معنى كل مفتاح Key في [مرجع إعدادات Gitea](https://docs.gitea.com/administration/config-cheat-sheet/?ref=arabroot.io)، وطريقة استخدام هذه المتغيرات في [دليل التثبيت بـ Docker](https://docs.gitea.com/installation/install-with-docker/?ref=arabroot.io). والملف سوف يكون كما يلي:

```yaml
services:
  db:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: gitea
      POSTGRES_PASSWORD: ${GITEA_DB_PASSWORD:?}
      POSTGRES_DB: gitea
    volumes:
      - db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U gitea -d gitea"]
      interval: 10s
      timeout: 5s
      retries: 10

  gitea:
    image: gitea/gitea:1.27.3
    restart: unless-stopped
    environment:
      USER_UID: "1000"
      USER_GID: "1000"
      GITEA__database__DB_TYPE: postgres
      GITEA__database__HOST: db:5432
      GITEA__database__NAME: gitea
      GITEA__database__USER: gitea
      GITEA__database__PASSWD: ${GITEA_DB_PASSWORD}
      GITEA__server__DOMAIN: git.example.com
      GITEA__server__SSH_DOMAIN: git.example.com
      GITEA__server__ROOT_URL: https://git.example.com/
      # المنفذ الذي يظهر في روابط الاستنساخ
      GITEA__server__SSH_PORT: "2222"
      # المنفذ الذي يستمع عليه SSH داخل الحاوية
      GITEA__server__SSH_LISTEN_PORT: "22"
    ports:
      - "127.0.0.1:3000:3000"
      - "2222:22"
    volumes:
      - data:/data
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:3000/api/healthz"]
      interval: 30s
      timeout: 5s
      retries: 5
    networks:
      - default
      - proxy

volumes:
  db:
  data:

networks:
  proxy:
    external: true
```

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

- منفذ الويب مربوط بالعنوان `127.0.0.1` فقط، والسبب أن الـ Reverse Proxy وحده هو الذي يصل إليه، فلا يستطيع أحد أن يتجاوز شهادة TLS ويفتح Gitea مباشرة على المنفذ 3000.
- منفذ SSH متاح على `2222` لكل الشبكة، وهذا مقصود لأن المطورين يتصلون به مباشرة من أجهزتهم، واتصالات SSH لا تمر بالـ Reverse Proxy أصلاً.
- قيمة `SSH_PORT` هي المنفذ الذي يكتبه Gitea في روابط الاستنساخ مثل `ssh://git@git.example.com:2222/…`، وأما `SSH_LISTEN_PORT` فهو المنفذ الذي يستمع Listen عليه SSH داخل الـ Container، ولذلك يبقى 22 في الداخل و2222 في الخارج.
- المتغير `ROOT_URL` هو العنوان الرئيسي للموقع Root URL الذي تبنى منه كل الروابط في الواجهة والبريد، ويجب أن يطابق العنوان الذي يكتبه المستخدم في المتصفح.
- الصيغة `${GITEA_DB_PASSWORD:?}` تجعل Compose يتوقف برسالة خطأ إذا لم يجد المتغير في ملف `.env`، بدل أن يشغل قاعدة البيانات بكلمة مرور فارغة.
- الشرط `service_healthy` يجعل Gitea ينتظر حتى تصبح PostgreSQL جاهزة فعلاً، وأما الشبكة `proxy` فهي الشبكة المشتركة مع الـ Reverse Proxy، ولذلك هي من نوع `external`.

### تشغيل الخدمات Services

الآن سوف نقوم بإنشاء الشبكة المشتركة ثم تشغيل الخدمتين ومتابعة السجلات Logs:

```bash
docker network create proxy
docker compose up -d
docker compose logs -f gitea
```

وإذا كانت الشبكة Network موجودة من قبل فسوف يفشل الأمر الأول برسالة أنها موجودة، وهذا لا يضر بشيء، وأما إذا نسيت إنشاءها من الأساس فسوف يرفض Compose التشغيل برسالة `network proxy declared as external, but could not be found`، والحل أن تنشئها ثم تعيد الأمر. وبعد ذلك انتظر حتى تظهر الحالة `healthy` في مخرجات Output الأمر `docker compose ps`.

## ربط النطاق Domain وشهادة TLS عبر الـ Reverse Proxy

أنشئ المضيف Host في الـ Reverse Proxy قبل أن تفتح معالج التثبيت، والسبب أنك تريد أن يعمل كل شيء من البداية على العنوان النهائي، فلا تبقى في الإعدادات روابط قديمة من نوع localhost. وفي [Nginx Proxy Manager](https://nginxproxymanager.com/?ref=arabroot.io) تكون الإعدادات كما يلي:

- **Domain Names:** `git.example.com`، **Forward Hostname:** اسم الـ Container الخاص بـ gitea (مثل `gitea-gitea-1`)، **Port:** `3000`.
- فعل Websockets Support وBlock Common Exploits، واطلب شهادة Let's Encrypt مع Force SSL.

في الإعداد المتقدم Advanced قم بزيادة [الحد الأقصى لحجم الطلب](https://nginx.org/en/docs/http/ngx%5Fhttp%5Fcore%5Fmodule.html?ref=arabroot.io#client%5Fmax%5Fbody%5Fsize)، والسبب أن دفع Push مستودع كبير أو Container Image يتجاوز الحد الافتراضي (1 MB) بكثير:

```nginx
client_max_body_size 1024m;
```

وإذا كنت تستخدم Caddy فالإعداد المقابل يكون عبر [request\_body](https://caddyserver.com/docs/caddyfile/directives/request%5Fbody?ref=arabroot.io) و[reverse\_proxy](https://caddyserver.com/docs/caddyfile/directives/reverse%5Fproxy?ref=arabroot.io):

```nginx
git.example.com {
    request_body {
        max_size 1GB
    }
    reverse_proxy gitea-gitea-1:3000
}
```

وتذكر أن اتصالات SSH لا تمر بالـ Reverse Proxy، وإنما تصل مباشرة إلى المنفذ 2222 على السيرفر، وتجد أمثلة لبرامج أخرى في [دليل Gitea لإعداد الـ Reverse Proxy](https://docs.gitea.com/administration/reverse-proxies/?ref=arabroot.io).

## إكمال معالج التثبيت Installation Wizard

افتح `https://git.example.com/` في المتصفح، وسوف تجد أن إعدادات قاعدة البيانات مملوءة من متغيرات البيئة Environment Variables، فاتركها كما هي.

![قسم إعدادات قاعدة البيانات في معالج تثبيت Gitea مملوءاً بـ PostgreSQL وdb:5432](https://arabroot.io/content/images/2026/09/gitea-01-installer-database.webp)

إعدادات PostgreSQL مأخوذة من ملف Compose

وفي قسم **General Settings** غير **Site Title** إلى اسم شركتك، وتأكد أن الحقول **Server Domain** و**SSH Server Port** و**Gitea Base URL** فيها القيم الصحيحة، ففي بيئة الإنتاج Production تكون القيمتان `2222` و`https://git.example.com/`، ولاحظ أن الصورة التالية تعرض قيماً من بيئة محلية. وإذا ظهرت لك قيمة مختلفة في Server Domain فلا تقلق، لأن المتغيرات في ملف Compose تكتب فوقها عند كل تشغيل.

![حقول Server Domain وSSH Server Port وGitea Base URL في معالج التثبيت](https://arabroot.io/content/images/2026/09/gitea-02-installer-server.webp)

النطاق ومنفذ SSH والعنوان الأساسي

وفي قسم **Optional Settings** ثلاثة أمور مهمة:

- **Server and Third-Party Service Settings:** فعل **Disable Self-Registration** إذا كان السيرفر لفريقك فقط، والسبب أن أي زائر يستطيع بدونه أن ينشئ حساباً Account ويبدأ في إنشاء المستودعات، وفعل **Require Sign-In to View Pages** إذا كانت كل المستودعات داخلية.
- **Email Settings:** أدخل بيانات SMTP إذا كانت جاهزة، أو أضفها لاحقاً بمتغيرات البيئة كما في قسم البريد أدناه.
- **Administrator Account Settings:** هنا تنشئ حساب المدير Administrator، ولا تستخدم الاسم `admin` لأنه أول اسم يجربه أي شخص يحاول تخمين كلمات المرور، واختر اسماً آخر مثل `gitadmin`.

📌

في 10 يوليو 2025 لاحظ فريق Wiz برمجيات خبيثة على سيرفرات Gogs مكشوفة على الإنترنت، وGogs هو [المشروع الذي انفصل عنه Gitea في الأصل](https://docs.gitea.com/?ref=arabroot.io)، حيث استغل المهاجمون ثغرة CVE-2025-8110 التي تسمح لأي مستخدم مسجل بأن ينشئ مستودعاً فيه رابط رمزي Symlink يكتب ملفات خارج المستودع، ومن ذلك وصلوا إلى تنفيذ الأوامر على السيرفر، وكان التسجيل المفتوح Open Registration مفعلاً افتراضياً في كثير منها، فوجد الباحثون أكثر من 700 سيرفر مخترق من أصل نحو 1,400 سيرفر مكشوف، وبقيت الثغرة دون إصلاح حتى صدر الإصدار 0.13.4 في 23 يناير 2026، وذلك بحسب [تقرير Wiz](https://www.wiz.io/blog/wiz-research-gogs-cve-2025-8110-rce-exploit?ref=arabroot.io). والدرس هنا أن كل حساب جديد على سيرفر Git هو شخص يستطيع أن يدفع ملفات إلى سيرفرك، لذلك لا تترك التسجيل مفتوحاً إلا إذا كنت تريد ذلك فعلاً.

![قسم Administrator Account Settings مع اسم المستخدم gitadmin والبريد وكلمة المرور](https://arabroot.io/content/images/2026/09/gitea-03-installer-admin.webp)

إنشاء حساب المدير أثناء التثبيت

بعد ذلك اضغط **Install Gitea**، فيكتب Gitea الإعدادات في الملف `/data/gitea/conf/app.ini` داخل الـ Volume، ويضبط `INSTALL_LOCK = true` حتى لا يظهر المعالج مرة أخرى لأي شخص يفتح الموقع، ثم يدخلك مباشرة بحساب المدير.

## أول مستودع Repository والعمل عبر SSH

أضف أولاً مفتاحك العام Public Key، وذلك بأن تنقر على صورة حسابك وتفتح **Settings ← SSH / GPG Keys ← Add Key**، ثم تلصق محتوى الملف `~/.ssh/id_ed25519.pub`، وإذا لم يكن لديك مفتاح فأنشئه بهذا الأمر:

```bash
ssh-keygen -t ed25519 -C "admin@example.com"
```

ثم اختر **New Repository** من زر **+** في أعلى الصفحة، وأدخل الاسم والوصف، وفعل **Initialize Repository** إذا أردت أن يبدأ المستودع بملف README.

![نموذج New Repository في Gitea مع اسم المستودع demo-app والوصف](https://arabroot.io/content/images/2026/09/gitea-04-new-repository.webp)

إنشاء أول مستودع

وفي صفحة المستودع سوف تجد زر **Code** الذي يعرض رابط الاستنساخ Clone URL عبر SSH، ولاحظ أن المنفذ الذي حددته في ملف Compose موجود في الرابط تلقائياً:

![صفحة المستودع demo-app مع قائمة Clone ورابط ssh يتضمن المنفذ](https://arabroot.io/content/images/2026/09/gitea-05-repo-ssh-clone.webp)

يتضمن رابط SSH المنفذ غير القياسي تلقائياً

```bash
git clone ssh://git@git.example.com:2222/gitadmin/demo-app.git
cd demo-app
echo "hello" > hello.txt
git add hello.txt
git commit -m "add hello"
git push
```

وحتى لا تكتب المنفذ في كل مرة، أضف هذه الأسطر إلى الملف `~/.ssh/config` على جهازك:

```ini
Host git.example.com
    Port 2222
    User git
    IdentityFile ~/.ssh/id_ed25519
```

وبعدها تعمل الصيغة المختصرة `git clone git@git.example.com:gitadmin/demo-app.git` كما تعودت عليها في GitHub.

💡

وقد تريد روابط بدون منفذ (`git@git.example.com:…` على المنفذ 22)، وهنا يوجد حلان: أن تنقل خدمة SSH الخاصة بالسيرفر إلى منفذ آخر وتعطي المنفذ 22 لـ Gitea، أو أن تستخدم «SSH passthrough» الموثق في دليل تثبيت Gitea عبر Docker، ولكن الحلين يحتاجان إلى حذر لأن أي خطأ فيهما قد يقفل عليك باب الدخول إلى السيرفر نفسه، لذلك يبقى المنفذ 2222 هو الخيار الأبسط والأسلم.

## لوحة إدارة الموقع Site Administration

انقر على صورة حسابك وافتح **Site Administration**، ومن هنا تدير المستخدمين والمنظمات والمستودعات، ومصادر المصادقة Authentication Sources مثل LDAP وOAuth2، والـ Runners، وعمليات الصيانة Maintenance، وتجد ملخص الإعدادات الفعلية التي يعمل بها السيرفر تحت **Configuration ← Summary**.

![صفحة Site Administration في Gitea مع قائمة عمليات الصيانة](https://arabroot.io/content/images/2026/09/gitea-06-site-administration.webp)

لوحة إدارة الموقع وعمليات الصيانة

وأنشئ لفريقك **منظمة** (New Organization) وضع المستودعات تحتها، ولا تضعها تحت حسابات الأفراد، والسبب أن المستودع الذي تحت حساب شخص يرتبط به، فإذا غادر الشركة وأردت حذف حسابه أو تعطيله فسوف تضطر إلى نقل مستودعاته أولاً، وأما مستودعات المنظمة فتبقى في مكانها مهما تغير أعضاء الفريق.

## إعداد البريد Email وبقية الإعدادات

يحتاج Gitea إلى البريد لإرسال الإشعارات Notifications واستعادة كلمات المرور، والطريقة الأنظف هي أن تضيف الإعدادات إلى قسم `environment` بنفس الصيغة السابقة، وذلك بحسب [دليل إعداد البريد](https://docs.gitea.com/administration/email-setup/?ref=arabroot.io)، ثم تنفذ `docker compose up -d`:

```yaml
      GITEA__mailer__ENABLED: "true"
      GITEA__mailer__PROTOCOL: smtp+starttls
      GITEA__mailer__SMTP_ADDR: smtp.example.com
      GITEA__mailer__SMTP_PORT: "587"
      GITEA__mailer__USER: git@example.com
      GITEA__mailer__PASSWD: ${GITEA_SMTP_PASSWORD}
      GITEA__mailer__FROM: "Example Git <git@example.com>"
      GITEA__service__DISABLE_REGISTRATION: "true"
      GITEA__service__ENABLE_NOTIFY_MAIL: "true"
```

ولاحظ أن كلمة مرور البريد تأتي من المتغير `GITEA_SMTP_PASSWORD`، لذلك أضفه إلى ملف `.env` بجانب كلمة مرور قاعدة البيانات، وبعد ذلك اختبر الإرسال من **Site Administration ← Configuration ← Summary ← Mailer Configuration ← Send Testing Email**.

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

- الأمر `docker compose ps` يعرض الـ Containers الاثنين بحالة `healthy`، والأمر `curl -s https://git.example.com/api/healthz` يعيد `"status": "pass"`.
- صفحة الدخول لا يوجد فيها زر التسجيل Register بعد تعطيل التسجيل الذاتي.
- الاستنساخ والدفع يعملان عبر SSH وHTTPS، ورابط الاستنساخ في الواجهة يبدأ بـ `https://git.example.com/`.
- صفحة **Site Administration ← Self Check** لا توجد فيها تحذيرات.

اختبار SSH يرحب بك باسمك:

```bash
ssh -T -p 2222 git@git.example.com
```

والمخرج سوف يكون كما يلي: `Hi there, gitadmin! You've successfully authenticated…`، ويكمل السطر بأن Gitea لا يعطي صلاحية Shell، وهذا طبيعي.

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

قد يبدو النسخ الاحتياطي من الوهلة الأولى بسيطاً: تأخذ نسخة من قاعدة البيانات وتنتهي، ولكن هذا الحل غير مناسب إطلاقاً، لأن الكود نفسه ليس في قاعدة البيانات، وإنما في مجلد المستودعات على الـ Volume، ومعه المرفقات والحزم وملف الإعدادات. والحل الثاني أن تنسخ الـ Volume كاملاً بينما Gitea يعمل، وهذا أيضاً قد يعطيك نسخة لا تتطابق فيها الملفات مع قاعدة البيانات. لذلك فالحل الأنسب هو الأمر [gitea dump](https://docs.gitea.com/administration/backup-and-restore/?ref=arabroot.io) الذي يجمع في ملف واحد المستودعات، ومجلد البيانات بما فيه المرفقات Attachments وLFS والحزم والصور الرمزية Avatars، والملف `app.ini`، ونسخة SQL من قاعدة البيانات، ويجب أن تشغله بالمستخدم `git` وليس بالمستخدم root:

```bash
cd /opt/gitea
docker compose exec gitea sh -c 'mkdir -p /data/backups && chown git:git /data/backups'
docker compose exec -u git -w /tmp gitea gitea dump -c /data/gitea/conf/app.ini --type tar.gz -f /data/backups/gitea-dump-$(date +%F).tar.gz
```

ولاحظ أن التوثيق يوصي بأن تأخذ النسخة والسيرفر في وقت هادئ، والسبب أن عملية مثل ترحيل مستودع Migration من خدمة أخرى إذا وقعت في منتصف النسخ فقد تجد المستودع ناقصاً بينما قاعدة البيانات تقول إنه كامل، لذلك اجعل النسخة في ساعة لا يعمل فيها أحد. وبعد ذلك انقل الملف إلى خارج الـ Container، ثم إلى خارج السيرفر أيضاً، لأن النسخة التي على نفس السيرفر تضيع معه:

```bash
docker compose cp gitea:/data/backups/gitea-dump-$(date +%F).tar.gz ./
docker compose exec gitea sh -c 'rm -f /data/backups/*.tar.gz'
```

والناتج أرشيف Archive فيه `repos/` و`data/` و`app.ini` و`gitea-db.sql`. وخذ مع هذا الأرشيف نسخة من PostgreSQL [بأداتها الأصلية](https://www.postgresql.org/docs/17/app-pgdump.html?ref=arabroot.io)، والسبب أن ملف SQL الذي يصدره `gitea dump` يمر عبر طبقة Gitea نفسها، والتوثيق يذكر أن استعادته قد تواجه مشكلات، وأما نسخة `pg_dump` فاستعادتها أسرع وأضمن:

```bash
docker compose exec -T db pg_dump -U gitea -d gitea -Fc > gitea-db-$(date +%F).dump
```

**الاستعادة** تكون على سيرفر جديد بنفس الإصدار Version، وبعد أن تنشئ عليه نفس ملف Compose وملف `.env`، والخطوات كما يلي:

```bash
cd /opt/gitea
docker compose up -d db
docker compose exec -T db pg_restore -U gitea -d gitea --clean --if-exists --no-owner < gitea-db-2026-09-26.dump
mkdir -p restore && tar xzf gitea-dump-2026-09-26.tar.gz -C restore
docker compose run --rm --no-deps -v "$PWD/restore:/restore" --entrypoint sh gitea -c '
  mkdir -p /data/git/.ssh /data/gitea/conf &&
  cp -a /restore/repos/. /data/git/repositories/ &&
  cp -a /restore/data/. /data/gitea/ &&
  cp /restore/app.ini /data/gitea/conf/app.ini &&
  chown -R git:git /data'
docker compose up -d
docker compose exec -u git gitea gitea admin regenerate hooks
docker compose exec -u git gitea gitea admin regenerate keys
```

في الأوامر أعلاه لاحظ التالي:

- الأمر `regenerate hooks` يعيد إنشاء الـ Git Hooks بالمسارات الجديدة، ولا بد منه بعد النقل وإلا فشل الدفع إلى المستودعات.
- الأمر `regenerate keys` يعيد كتابة الملف `/data/git/.ssh/authorized_keys` من المفاتيح المحفوظة في قاعدة البيانات، والسبب أن هذا الملف ليس ضمن الأرشيف، فإذا نسيت هذه الخطوة فسوف ترى الواجهة تعمل والمستودعات موجودة، ولكن كل اتصال SSH يرجع بالخطأ `Permission denied (publickey)`.
- المجلد `/data/git/.ssh` ننشئه قبل `chown` حتى يكون ملكاً للمستخدم `git`، لأن الـ Image إذا وجدت المجلد `/data/git` مملوكاً للمستخدم `git` لا تعيد ضبط ملكية ما بداخله، فينشأ `.ssh` باسم root ويفشل الأمر السابق بخطأ صلاحيات.

وبعد الاستعادة تأكد أن المستودعات عادت بملفاتها، وأن حساب المدير يعمل بكلمة مروره، وأن الاستنساخ عبر SSH يعمل. ونفذ الاستعادة كاملة على سيرفر تجريبي Test Server مرة واحدة على الأقل قبل أن تحتاجها فعلاً، لأن النسخة التي لم تجرب استعادتها لا تعرف هل تعمل أم لا.

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

عندما تشغل إصداراً أحدث يقوم Gitea بترحيل Migrate قاعدة البيانات تلقائياً إلى الشكل الجديد، وبعد الترحيل [لا يمكن الرجوع إلى الإصدار الأقدم](https://docs.gitea.com/installation/upgrade-from-gitea/?ref=arabroot.io) إلا من نسخة احتياطية، لذلك التزم بهذا الترتيب:

1. اقرأ قسم **BREAKING** في ملاحظات الإصدار Release Notes، ففي [الإصدار 1.27](https://github.com/go-gitea/gitea/releases/tag/v1.27.0?ref=arabroot.io) مثلاً تغير دعم reusable workflows في Actions، وصار Gitea يستخدم Content-Security-Policy مع nonce، وهذا قد يؤثر في القوالب المخصصة Custom Templates التي فيها سكربتات Scripts.
2. خذ النسختين الاحتياطيتين (`gitea dump` و`pg_dump`).

غير الوسم Tag في `docker-compose.yml` إلى الإصدار الجديد (مثل `gitea/gitea:1.27.4` أو `gitea/gitea:28.0.0`)، ثم نفذ:

```bash
docker compose pull gitea
docker compose up -d gitea
docker compose logs -f gitea
```

والإصدارات الترقيعية Patch Releases من نفس السلسلة (1.27.x) آمنة عادة، لأنها لا تغير شكل قاعدة البيانات، وأما بين الإصدارات الرئيسية Major Releases فيمكنك أن تنتقل مباشرة إلى الأحدث، ولكن بعد أن تقرأ ملاحظات كل إصدار بينهما. وإذا أردت أن تحدث PostgreSQL إلى إصدار رئيسي جديد، فاستخدم `pg_dump` ثم الاستعادة في Container بالإصدار الجديد، ولا تكتف بتغيير الوسم وحده، لأن PostgreSQL لا تحدث مجلد بياناتها من تلقاء نفسها.

ويجدر بك أن تعرف أن Gitea أصدر في 30 سبتمبر 2026 [الإصدار 28.0.0](https://blog.gitea.com/release-of-28.0.0/?ref=arabroot.io)، وهو الإصدار الذي كان سيسمى 1.28 لولا أن المشروع حذف البادئة «1.» من أرقام الإصدارات، وفيه إصلاحات أمنية، والملف أعلاه يعمل عليه دون تعديل، وينتقل إليه من 1.27.3 بتغيير الوسم فقط، ولكن قبل أن تنتقل انتبه لهذه التغييرات:

- لم يعد Gitea يقرأ `DOMAIN`، وإنما يأخذ النطاق من `ROOT_URL`، وهذا لا يؤثر على الملف أعلاه لأن `ROOT_URL` و`SSH_DOMAIN` مكتوبان فيه صراحة.
- صار التسجيل الذاتي معطلاً افتراضياً، وصارت الإشعارات Notifications في الواجهة تعمل عبر WebSocket، لذلك تأكد أن Websockets Support مفعل في الـ Reverse Proxy.
- صار Gitea يحذف سجلات تشغيل Actions بعد 400 يوم افتراضياً، فإذا أردت الاحتفاظ بها فاضبط `GITEA__actions__RUN_RETENTION_DAYS` على `0` قبل التحديث، وتغيرت أيضاً إعدادات الاتصالات الخارجية Egress الخاصة بالترحيل والمرايا Mirrors.

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

### روابط الاستنساخ تظهر بالمنفذ 22 أو بدون منفذ

والسبب واحد من اثنين: `SSH_PORT` غير مضبوط، أو القيمة في `app.ini` قديمة، ولأن متغيرات مثل `GITEA__server__SSH_PORT` تكتب فوق `app.ini` عند كل تشغيل، فيكفي أن تعيد إنشاء الـ Container بالأمر `docker compose up -d`.

### الخطأ «Permission denied (publickey)» عبر SSH

إما أن المفتاح غير مضاف إلى حسابك، أو أنك تتصل بالمنفذ 22 وهو منفذ SSH الخاص بالسيرفر وليس المنفذ 2222، أو أنك استعدت نسخة احتياطية ولم تنفذ `gitea admin regenerate keys`. ولمعرفة السبب نفذ `ssh -vT -p 2222 git@git.example.com`، وانظر أي مفتاح يعرضه جهازك Client على السيرفر.

### الخطأ «413 Request Entity Too Large» عند الدفع أو رفع حزمة

والسبب هو حد حجم الطلب في الـ Reverse Proxy، والحل أن تضيف `client_max_body_size` كما في قسم الـ Reverse Proxy أعلاه.

### gitea dump يفشل بخطأ «permission denied» أو «Gitea is not supposed to be run as root»

شغل الأمر بالخيار `-u git`، ومن مجلد يملكه المستخدم `git`، والسبب أن Gitea يرفض العمل بالمستخدم root، وأن الأمر يكتب ملفات مؤقتة في المجلد الحالي، ولهذا نستخدم `-w /tmp` والمجلد `/data/backups`.

### تحذير ROOT\_URL أو روابط تشير إلى localhost

يجب أن يطابق `ROOT_URL` العنوان العام الذي يكتبه المستخدم في المتصفح تماماً، مع `https://` في أوله والشرطة المائلة Trailing Slash في آخره كما في الملف أعلاه، وإلا فسوف تظهر روابط خاطئة في البريد، ويفشل تسجيل الدخول عبر OAuth، ولا تستطيع الـ Runners الاتصال بالسيرفر.

## الخلاصة

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

- مستودعات Git العادية على السيرفر لا تكفي لفريق يحتاج إلى صلاحيات ومراجعة كود، وGitLab ثقيل على فريق صغير، وGitea يعطيك تجربة قريبة من GitHub في Container واحد.
- اربط منفذ الويب بالعنوان `127.0.0.1` خلف الـ Reverse Proxy، وشغل SSH على المنفذ 2222، واضبط `ROOT_URL` و`SSH_PORT` بدقة لأن منهما تبنى كل الروابط.
- عطل التسجيل الذاتي إذا كان السيرفر لفريقك فقط، ولا تستخدم `admin` اسماً لحساب المدير.
- خذ نسخة `gitea dump` ونسخة `pg_dump` وانقلهما خارج السيرفر، وبعد الاستعادة لا تنس `regenerate hooks` و`regenerate keys`.
- اقرأ ملاحظات الإصدار قبل كل تحديث رئيسي، وخاصة الإصدار 28.0.0 الذي غير طريقة الترقيم وبعض الإعدادات الافتراضية.

## سجل التحديثات

- سبتمبر 2026: كتابة الدليل واختباره على [Gitea 1.27.3](https://github.com/go-gitea/gitea/releases/tag/v1.27.3?ref=arabroot.io).
- أكتوبر 2026: مراجعة الدليل، وإضافة خطوة `regenerate keys` وإنشاء المجلد `.ssh` إلى الاستعادة، وملاحظات التحديث إلى Gitea 28.0.0.