[{"data":1,"prerenderedAt":233},["ShallowReactive",2],{"seo-verification":3,"blog-host-gitea-on-your-own-vps-ma-en":6,"promo-bar-likely":232},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-host-gitea-on-your-own-vps-ma-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"noindex":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":33,"intro":36,"sections":37,"ctaTitle":170,"ctaBody":171,"ctaButton":172,"ctaUrl":173,"relatedPosts":174},74,"host-gitea-on-your-own-vps",{"fr":12,"en":10,"ar":13,"es":14},"heberger-gitea","استضافة-gitea-على-خادم-vps-الخاص-بك","alojar-gitea-en-un-vps","Host Gitea on your own VPS in 2026","Deploy Gitea 1.25 on a VPS with Docker and SSL. Full guide: Gitea Actions CI\u002FCD, Gitea vs Forgejo v16 comparison, daily administration, SSH and HTTPS troubleshooting.",13,1,false,"2026-04-07T00:00:00+00:00","2026-11-08T16:32:50+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},7,"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fheberger-gitea-poster.svg",{"categorySlug":34,"appSlug":35},"development","gitea","Gitea is a self-hosted Git forge written in Go, renowned for being lightweight and fast. Deploying it on your own VPS gives you a private GitHub with no per-user subscription and no limit on private repositories. In 2026, Gitea 1.25 is the current version and Forgejo v16 has established itself as the reference community fork — this guide covers Gitea installation, setting up CI\u002FCD pipelines with Gitea Actions, the comparison with Forgejo, daily administration, and troubleshooting the most common errors.",[38,42,52,55,77,80,83,123,144,147,150,153,156,159,162,164,167],{"type":39,"title":40,"body":41},"h2","Why self-host Gitea on a VPS","Gitea uses barely 200 to 300 MB of RAM at idle, making it one of the most efficient Git forges to self-host. On a VPS, you keep full control of your source code: no data is analyzed by a third party, no arbitrary limit on the number of private repositories or collaborators, and no cost that climbs with your team. For an agency or an independent developer, a single VPS can host all client projects with permissions partitioned by organization. Gitea natively includes CI\u002FCD compatible with GitHub Actions workflows (Gitea Actions), a package registry and a web editor, which covers almost the entire development cycle with no external dependency.",{"type":43,"title":44,"items":45},"ul","Concrete benefits of a self-hosted Gitea",[46,47,48,49,50,51],"Minimal memory footprint: runs comfortably on a 2 GB RAM VPS even with several dozen users.","Private repositories without per-seat billing or imposed limits, unlike SaaS offerings.","Gitea Actions built in to run your CI\u002FCD pipelines without an external service.","Package registry (Docker, npm, Composer, Maven) hosted on the same instance.","Fine-grained authentication: LDAP, OAuth2, personal access tokens and per-user SSH keys.","Trivial backup: everything fits in one data volume and a database.",{"type":39,"title":53,"body":54},"Hardware and software prerequisites","Gitea is very frugal. For a small team (up to 20 users), a VPS with 2 vCPU and 2 GB of RAM is more than enough; count on 4 GB if you enable Gitea Actions with local runners that compile code. Plan for at least 20 to 40 GB of SSD storage depending on the size of your repositories. On the software side: Docker Engine and the Docker Compose plugin installed, a domain name (or a subdomain such as `git.yourdomain.com`) pointing to the VPS IP via an A record, and port 22 reserved for the server's SSH — Gitea will expose its own SSH on another port to avoid the conflict.",{"type":56,"title":57,"steps":58},"steps","Deploy Gitea with Docker and SSL",[59,62,65,68,71,74],{"title":60,"body":61},"Prepare the VPS and Docker","Connect via SSH, update the system then install Docker and the Compose plugin. Create a dedicated directory: `mkdir -p \u002Fopt\u002Fgitea && cd \u002Fopt\u002Fgitea`. Also create a data volume that will survive container updates.",{"title":63,"body":64},"Write the docker-compose.yml","Define two services: `gitea` (image `gitea\u002Fgitea:1.25`) and a `postgres:16` database. Mount `.\u002Fgitea:\u002Fdata` for persistence, set `USER_UID`\u002F`USER_GID` to 1000, and map Gitea's SSH to host port 2222: `\"2222:22\"`. Leave the HTTP port 3000 internal; it will be served by the reverse proxy.",{"title":66,"body":67},"Launch the containers","Run `docker compose up -d` then `docker compose logs -f gitea` to follow the initialization. Check that the connection to PostgreSQL succeeds before continuing.",{"title":69,"body":70},"Configure the reverse proxy","With Caddy, a single line is enough: `git.yourdomain.com { reverse_proxy localhost:3000 }`. Caddy automatically obtains and renews the Let's Encrypt certificate. With Nginx, create a `server` that proxies to `http:\u002F\u002F127.0.0.1:3000` and use `certbot --nginx` for SSL.",{"title":72,"body":73},"Finalize the web installation","Open `https:\u002F\u002Fgit.yourdomain.com`, complete the wizard by filling in the HTTPS base URL and the database host (`db:5432`). Set `ROOT_URL` correctly, otherwise the clone links will be wrong. Expand \"Optional Settings > Administrator Account Settings\", create your admin account there, then submit.",{"title":75,"body":76},"Secure repository SSH","Configure your clients to clone via `ssh:\u002F\u002Fgit@git.yourdomain.com:2222\u002F...`, or add a `Host` block in `~\u002F.ssh\u002Fconfig` to hide the port. Disable public sign-up in the admin if the instance is private.",{"type":39,"title":78,"body":79},"Gitea Actions: self-hosted CI\u002FCD on your VPS","Gitea Actions is Gitea's native CI\u002FCD engine, compatible with GitHub Actions workflow syntax. It lets you run pipelines directly on your infrastructure, with no dependency on an external service.\n\nTo enable it, add to your `app.ini`:\n\n```ini\n[actions]\nENABLED = true\n```\n\nThen register an `act_runner` runner in a separate container on the same VPS. Cap its RAM via `mem_limit` so that a heavy build does not choke Gitea itself. For heavy pipelines (compilation, integration tests), dedicate a second VPS to the runner instead.\n\nA minimal workflow for a Node.js project goes in `.gitea\u002Fworkflows\u002Fci.yml`:\n\n```yaml\non: [push, pull_request]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: '22'\n      - run: npm ci\n      - run: npm test\n```\n\nThe runner executes this workflow on every push. You can combine multiple jobs, use version matrices, and share artifacts between steps — exactly like GitHub Actions, but on your own server.",{"type":81,"body":82},"tip","Cap the `act_runner` container RAM to 2 GB via `mem_limit: 2g` in your `docker-compose.yml` if your VPS has 4 GB total. That way, even a build that consumes everything allocated to it still leaves 2 GB for Gitea — enough to keep serving pushes and pull requests during compilation. For builds that produce large artifacts (Docker images, binaries), add a separate volume mounted at `\u002Fhome\u002Frunner\u002Fwork` to avoid filling Gitea's main data volume.",{"type":84,"title":85,"headers":86,"rows":90},"comparison","Gitea or Forgejo in 2026: which one to choose?",[87,88,89],"Criterion","Gitea 1.25","Forgejo v16",[91,95,99,103,107,111,115,119],[92,93,94],"Governance","Commercial company (Gitea Ltd)","Non-profit association (Codeberg e.V.)",[96,97,98],"License","MIT","MIT — 100% free software, no enterprise edition",[100,101,102],"Recommended by Awesome-Selfhosted","No (removed in 2022)","Yes — default recommendation since 2024",[104,105,106],"ActivityPub federation","Not planned","Rolling out since Forgejo v7 (April 2024), GA in v16",[108,109,110],"Gitea API compatibility","Reference","Compatible: same API, same webhooks, same data format",[112,113,114],"Security — public audits","Rare","Code audits published by the Codeberg community",[116,117,118],"Community roadmap","Driven by Gitea Ltd","Open governance, public RFCs, contributor voting",[120,121,122],"Migration from Gitea","N\u002FA","No data loss: same database schema and same volume format",{"type":56,"title":124,"steps":125},"Migrate from Gitea to Forgejo",[126,129,132,135,138,141],{"title":127,"body":128},"Back up your Gitea instance","Before anything, create a full dump: `docker exec -u git gitea gitea admin dump -c \u002Fdata\u002Fgitea\u002Fconf\u002Fapp.ini`. Retrieve the generated archive outside the container and verify that the `.\u002Fgitea` volume is included in your VPS snapshot.",{"title":130,"body":131},"Check version compatibility","Forgejo v16 supports migration from Gitea 1.20 and later. If your instance is running an older version, first upgrade to Gitea 1.21 or 1.22 via `docker compose pull && docker compose up -d`, then verify there are no errors in the logs before proceeding.",{"title":133,"body":134},"Replace the Docker image","In your `docker-compose.yml`, replace `image: gitea\u002Fgitea:latest` with `image: codeberg.org\u002Fforgejo\u002Fforgejo:latest`. The data volume (`.\u002Fgitea:\u002Fdata`) and the PostgreSQL database remain unchanged — Forgejo reads the same schema and the same `app.ini`.",{"title":136,"body":137},"Restart and let it migrate","Run `docker compose pull && docker compose up -d`. Forgejo automatically applies the necessary schema migrations at startup. Follow `docker compose logs -f forgejo` until the message indicating that the HTTP server is listening on port 3000.",{"title":139,"body":140},"Regenerate SSH keys","As with any upgrade that changes the binary referenced in `authorized_keys`, regenerate the entries: `docker exec -u git forgejo forgejo admin regenerate keys`. Then verify that a `git clone` over SSH succeeds from a client machine.",{"title":142,"body":143},"Test and validate","Open the web interface, check repositories, webhooks, and Forgejo Actions runners. `act_runner` runners registered under Gitea are compatible — reconnect them to the new registration token if the key has changed.",{"type":81,"title":145,"body":146},"Forgejo v16: key highlights (July 2026)","Forgejo v16.0.0 brings ActivityPub federation to general availability for issues and pull requests: an issue opened on one Forgejo instance can receive comments from users on another instance, without a shared account. The release also includes an instance-level secrets manager (AES-256 encryption at rest), a new code search engine based on the Bleve v2 index with regex support, and significant improvements to the background task scheduler. On the security front, API tokens are now hashed in the database (bcrypt) instead of stored in plaintext — an automatic migration at startup converts existing tokens.",{"type":39,"title":148,"body":149},"Daily administration: webhooks, repositories and orphan cleanup","Once Gitea is in production, a few maintenance operations come up regularly.\n\n**Managing webhooks.** Gitea logs the history of every delivery in \"Repository Settings → Webhooks → Recent Deliveries\". A failed webhook (timeout, HTTP 5xx error) can be replayed manually from that history. If your runners run on the same private network as Gitea, add their IP ranges to `ALLOWED_HOST_LIST` in the `[webhook]` section of `app.ini` — otherwise calls to `localhost` are blocked by default since Gitea 1.20.\n\n**Purging orphaned repositories.** A repository deleted through the interface leaves its Git data (`objects\u002F`) on disk until the next background task run. Force an immediate pass from the admin: \"Site Administration → Background Tasks → Run: Purge Deleted Repositories\". The freed space only becomes visible after `docker exec -u git gitea gitea admin storage --remove-unlisted`.\n\n**Storage quota per organization.** For multi-tenant instances, set a repository quota per organization in `app.ini` under `[repository]`: `MAX_CREATION_LIMIT = 50`. This prevents an account from creating hundreds of repositories unmonitored and filling the data volume.",{"type":39,"title":151,"body":152},"Advanced troubleshooting: SSH and HTTPS cloning","**`Permission denied (publickey)` after an upgrade.** The binary referenced in `authorized_keys` has changed path (verified in 1.27). The Gitea service responds normally over HTTPS, which masks the problem. Fix with: `docker exec -u git gitea gitea admin regenerate keys`. Verify that a `git clone` over SSH succeeds before declaring the upgrade complete.\n\n**Timeout or SSL error during HTTPS clone.** Three common causes: the `ROOT_URL` in `app.ini` does not match the actual HTTPS domain (broken links + possible redirect loop), the reverse proxy does not forward the `X-Forwarded-Proto: https` header (Gitea then generates HTTP URLs), or the Let's Encrypt certificate has expired (Caddy and certbot renew it automatically, but only if DNS still points to the VPS). Check `ROOT_URL` first: it is the most common cause after a domain migration.\n\n**Slow or stalling SSH clone.** The SSH upload-pack uses the same Git process as web operations. If your `act_runner` is using all available CPU cores during a build, Git operations may end up waiting for CPU. Add `cpus: '1.5'` to the runner in the compose to reserve cores without starving it completely.\n\n**`Database migration failed: column already exists`.** The database received a partial migration (stopped mid-upgrade). Restore the dump taken before the upgrade, start from a clean volume, then restart.",{"type":39,"title":154,"body":155},"Gitea 28.1.0: PR comparison fails with exit 128","After upgrading to Gitea 28.1.0, the pull request comparison view silently fails on large repositories (over 1.4 GB) with the error `exit 128 \"unable to fetch head branch\"`. The root cause is a stale lock file (`commit-graph.lock`) left behind by a `git fetch` run without the `--no-write-commit-graph` flag — a behavior introduced in this release (upstream issue #39795, reported October 15, 2026).\n\n**Diagnosis.** Check for the lock file in the affected repository:\n\n```bash\ndocker exec -u git gitea find \u002Fdata\u002Fgitea\u002Frepositories -name 'commit-graph.lock'\n```\n\n**Immediate fix.** Delete the stale lock file:\n\n```bash\ndocker exec -u git gitea find \u002Fdata\u002Fgitea\u002Frepositories -name 'commit-graph.lock' -delete\n```\n\nPR comparison is restored immediately without restarting the container.\n\n**Recommendation.** The upstream fix was merged in PR #39798 and is included in Gitea 28.1.1+. Plan the upgrade to 28.1.1 or later as soon as possible; instances hosting large repositories are systematically affected after a concurrent fetch.",{"type":39,"title":157,"body":158},"Gitea Actions: multiline YAML values corrupted since v28","Since Gitea v28, YAML folded block scalars (`>`) used in `with:` fields of workflow steps are mishandled at runtime: multiline values are truncated or concatenated without line breaks, producing silent errors that are difficult to diagnose (upstream issue #39775, reported October 15, 2026).\n\n**Typical symptom.** A `with:` parameter defined across multiple lines using `>`:\n\n```yaml\n- uses: some-action@v1\n  with:\n    config: >\n      line one\n      line two\n      line three\n```\n\n…is received by the action as `line one line two line three` (line breaks are lost), instead of the expected newline-separated value.\n\n**Workarounds.** Two equivalent approaches:\n\n1. Replace `>` (folded) with `|` (literal) — line breaks are then preserved as-is:\n\n```yaml\n  with:\n    config: |\n      line one\n      line two\n      line three\n```\n\n2. Use a quoted string with explicit `\\n` characters if the value must stay on a single logical line.\n\nThis behavior affects all workflows using `>` blocks inside `with:` since upgrading to v28; `|` blocks and inline scalars are not affected. An official fix is under evaluation in the upstream repository.",{"type":39,"title":160,"body":161},"Upgrading to Gitea 1.27 without losing SSH access","Version 1.27 changes the internal path of the `gitea` binary referenced inside `authorized_keys`. During the upgrade, existing entries still point to the old path: any clone or push over SSH silently fails with `Permission denied (publickey)`, without any server-side error message pointing to the upgrade. The Gitea service responds normally over HTTPS, which masks the problem. After every upgrade to 1.27 (or to any version that changes this path), run inside the container: `docker exec -u git gitea gitea admin regenerate keys`. The command rewrites all `authorized_keys` entries with the correct binary path. Verify that a `git clone` over SSH succeeds before declaring the upgrade complete.",{"type":81,"body":163},"`JWT_SECRET_URI` and `JWT_SECRET` must not coexist in `app.ini`. If both keys are present, Gitea or Forgejo loads one or the other depending on read order, silently invalidating all OAuth2 and Actions tokens issued before the upgrade. Users see intermittent authentication errors with no message pointing to `app.ini` as the cause. Choose one mechanism: `JWT_SECRET` (plaintext value) or `JWT_SECRET_URI` (path to a secret file), then remove the other. Restart the container after the change.",{"type":39,"title":165,"body":166},"Going further: Forgejo and advanced CI\u002FCD","If you want to push automation around your Git forge further, the article \u003Ca href=\"\u002Fen\u002Fblog\u002Fhost-forgejo-on-your-own-vps\">Host Forgejo on your VPS\u003C\u002Fa> covers setting up a Forgejo instance with dedicated Actions runners, configuring the built-in OCI registry, and security hardening (fail2ban, IP range restrictions). For teams wanting a complete continuous deployment pipeline, it is the natural complement to this guide.",{"type":81,"title":168,"body":169},"The official documentation","For advanced configuration and options specific to the tool, refer to the \u003Ca href=\"https:\u002F\u002Fdocs.gitea.com\" target=\"_blank\" rel=\"noopener noreferrer\">official Gitea documentation\u003C\u002Fa> or the \u003Ca href=\"https:\u002F\u002Fforgejo.org\u002Fdocs\u002Flatest\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">Forgejo documentation\u003C\u002Fa>. This guide covers going live on a VPS and migration; the vendor docs remain the reference for fine-tuning and specific use cases.","Deploy Gitea or Forgejo in minutes on a ServOrbit Cloud VPS","Our Cloud VPS, delivered with Docker preconfigured, lets you set up your Git forge with automatic SSL, without system tinkering. Dedicated resources, snapshots and a fixed IP included.","Launch my Cloud VPS","\u002Fvps-cloud",[175,192,209],{"id":176,"slug":177,"slugs":178,"title":182,"excerpt":183,"readTime":184,"views":185,"isPinned":19,"noindex":19,"publishedAt":186,"updatedAt":187,"category":188,"categories":189,"featuredImage":30,"bgImage":31,"posterImage":191,"relatedSolution":30},75,"host-forgejo-on-your-own-vps",{"fr":179,"en":177,"ar":180,"es":181},"heberger-forgejo","استضافة-forgejo-على-خادم-vps-الخاص-بك","alojar-forgejo-en-un-vps","Self-Host Forgejo on Your VPS: Complete 2025 Guide","Deploy Forgejo on your VPS with Docker, SSL, Actions runners and hardened security. Step-by-step guide for a sovereign Git forge.",10,9,"2026-04-06T00:00:00+00:00","2026-11-08T01:13:14+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[190],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fheberger-forgejo-poster.svg",{"id":193,"slug":194,"slugs":195,"title":199,"excerpt":200,"readTime":201,"views":18,"isPinned":19,"noindex":19,"publishedAt":202,"updatedAt":21,"category":203,"categories":204,"featuredImage":30,"bgImage":31,"posterImage":206,"relatedSolution":207},71,"hosting-nextcloud-on-your-own-vps",{"fr":196,"en":194,"ar":197,"es":198},"heberger-nextcloud","استضافة-nextcloud-على-خادمك-الـvps-الخاص","alojar-nextcloud-en-su-propio-vps","Self-Host Nextcloud on VPS: NC 35, PHP 8.3 and MariaDB 11 Guide","Host Nextcloud on a VPS: Docker Compose, Redis, MariaDB backup, uploads over 40 GB behind Cloudflare and an alternative to Office 2021.",20,"2026-04-10T00:00:00+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[205],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fheberger-nextcloud-poster.svg",{"categorySlug":25,"appSlug":208},"nextcloud",{"id":210,"slug":211,"slugs":212,"title":216,"excerpt":217,"readTime":218,"views":18,"isPinned":19,"noindex":19,"publishedAt":219,"updatedAt":220,"category":221,"categories":227,"featuredImage":30,"bgImage":31,"posterImage":229,"relatedSolution":230},43,"deploy-a-nodejs-application-on-a-vps",{"fr":213,"en":211,"ar":214,"es":215},"deployer-nodejs-vps","نشر-تطبيق-nodejs-على-vps","desplegar-una-aplicacion-nodejs-en-un-vps","Deploy a Node.js Application on a VPS","Deploy a Node.js application to production on a VPS: PM2, Nginx reverse proxy, Let's Encrypt SSL, and automatic startup at boot.",8,"2026-05-08T00:00:00+00:00","2026-11-08T01:13:12+00:00",{"id":222,"name":223,"slug":224,"color":225,"icon":226},4,"Development","developpement","bg-warning\u002F10 text-warning","dev",[228],{"id":222,"name":223,"slug":224,"color":225,"icon":226},"\u002Fblog\u002Fcovers\u002Fdeployer-nodejs-vps-poster.svg",{"categorySlug":34,"appSlug":231},"nodejs-stack",true,1794270409388]