Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Forgejo Ubuntu
GUIDEInstall Guides

How to Install Forgejo on Ubuntu 24.04 — Self-Hosted Git with Community Governance

22 min read

How to Install Forgejo on Ubuntu 24.04 — Self-Hosted Git with Community Governance

Forgejo is the community-governed Git forge that grew out of Gitea in late 2022. It gives you repositories, pull requests, issues, wikis, packages, releases, and a GitHub Actions compatible CI runner on a binary that fits on a floppy disk and runs comfortably on a 2 vCPU VPS. If you want the developer experience of GitHub without the vendor lock-in, without the price tag, and without shipping your source tree to a third party, Forgejo is the most credible option available today.

This tutorial walks through a production-shaped install on Ubuntu 24.04 LTS: a dedicated git system user, PostgreSQL as the backing store, the Forgejo binary under systemd, the first-run web wizard, a Forgejo Actions runner for CI, beta ActivityPub federation, and an Nginx reverse proxy with Let's Encrypt TLS.

Table of Contents

  • What is Forgejo?
  • Why Forgejo vs Gitea vs GitHub
  • Prerequisites
  • Step 1: Update the System
  • Step 2: Create the git System User
  • Step 3: Install PostgreSQL and Create the Database
  • Step 4: Download the Forgejo Binary
  • Step 5: Prepare Directories and app.ini
  • Step 6: Install the systemd Service
  • Step 7: Complete the First-Run Web Wizard
  • Step 8: Create Your First Repository
  • Step 9: Install the Forgejo Actions CI Runner
  • Step 10: Enable ActivityPub Federation (Beta)
  • Step 11: Nginx Reverse Proxy with TLS
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is Forgejo?

    Forgejo — pronounced "for-jay-oh", from the Esperanto verb forĝejo meaning "smithy" — is a self-hosted software forge. It was hard-forked from Gitea in December 2022 after Gitea's codebase was transferred into a for-profit company (Gitea Ltd) without community consultation. The Forgejo fork is now the flagship project of Codeberg e.V., a German non-profit that operates Codeberg.org, one of the largest public Forgejo instances.

    Under the hood Forgejo is a single Go binary that bundles:

    • A Git server (HTTP/S and SSH)
    • A web UI for browsing code, reviewing pull requests, triaging issues, and managing organizations
    • A REST API and a Swagger spec
    • A wiki engine
    • A container, npm, PyPI, Maven, NuGet, Debian, RPM, and generic package registry
    • Forgejo Actions, a YAML CI system that is largely compatible with GitHub Actions
    • ActivityPub federation (experimental) for cross-instance follows, stars, and discovery
    • Built-in OAuth2 provider, OIDC client, LDAP, SAML, and 2FA
    The project is released under the GPLv3+ and is developed entirely in the open on Codeberg itself. Releases follow a predictable cadence with long-term support branches — at the time of writing, v10.x is the current stable line and v7.x remains supported as an LTS.

    Why Forgejo vs Gitea vs GitHub

    Before you install anything, it is worth understanding where Forgejo sits in the ecosystem.

    Forgejo vs Gitea

    Gitea is still an excellent piece of software, and for a long time they were functionally identical because Forgejo rebased on Gitea commits. Since 2024 the codebases have diverged meaningfully. The practical differences:

    • Governance. Forgejo is owned by Codeberg e.V., a democratically run non-profit association. Gitea is owned by Gitea Ltd, a for-profit company. Neither is inherently wrong, but they produce different trade-offs over time — licensing changes, paid tiers, and CLA requirements are more likely under commercial ownership.
    • License. Forgejo is strictly GPLv3+ (copyleft). Gitea uses a permissive MIT license, which allows proprietary forks. If you care about downstream modifications being contributed back, Forgejo's license enforces it.
    • Federation. Forgejo has invested heavily in ActivityPub federation. Gitea has not prioritised this.
    • Accessibility and i18n. Forgejo has a dedicated accessibility team and has landed WCAG-oriented fixes faster than upstream.
    If you already run Gitea, you do not need to switch. If you are starting fresh and you care about long-term community governance, pick Forgejo.

    Forgejo vs GitHub

    GitHub is convenient and has the largest network effect in software. Self-hosting Forgejo makes sense when:

    • Your source code should not leave your infrastructure. Regulated industries, defence, healthcare, and teams with strict IP rules cannot ship source to a US-based SaaS.
    • You want predictable cost. GitHub Team is $4/user/month and GitHub Enterprise is $21/user/month. A 50-person team on Enterprise costs $12,600/year. A Forgejo VPS costs about $120/year and scales to thousands of users.
    • You want to own the data. No API deprecations, no policy changes, no rate limits on cloning your own repositories.
    • You want Actions without spend caps. Forgejo Actions minutes are free — they just use CPU on your runner.

    Forgejo vs GitLab

    GitLab is feature-rich but heavy. A self-managed GitLab Omnibus install wants 4 GB of RAM minimum (8 GB realistic) before a single user logs in. Forgejo runs a small team in 300 MB. GitLab is the right choice when you need its DevSecOps suite (SAST, DAST, container scanning) integrated out of the box. Forgejo is the right choice when you want a Git forge plus CI and nothing else, without the operational overhead.

    Forgejo Actions vs Drone CI

    Forgejo Actions uses GitHub Actions YAML and the existing actions/* ecosystem. Drone CI uses its own YAML and plugin model. If your engineers already know GitHub Actions, Forgejo Actions has zero learning curve. Drone remains a solid option for container-native pipelines, but Actions is now the default.

    Prerequisites

    • Ubuntu 24.04 LTS VPS with root or sudo access
    • SSH access — port 22 open, or whatever port you have hardened it to
    • At least 2 GB of RAM and 20 GB of disk for a small team (repos grow fast — budget generously)
    • A domain name pointing at your VPS (e.g. git.example.com) for the TLS step
    • Outbound internet for package installs and Let's Encrypt challenges
    Recommended Plan: Starter
    >
    For a team of 5 to 20 engineers with moderate CI usage, the Starter plan is the sweet spot:
    >
    - 2 vCPU cores
    - 4 GB RAM
    - 50 GB NVMe SSD
    - Unmetered bandwidth
    >
    Larger teams or heavier Actions workloads should scale up to 4 vCPU / 8 GB RAM, or offload runners onto dedicated VPS nodes.

    Connect to the server:

    bash
    ssh root@your-server-ip

    Step 1: Update the System

    Refresh package metadata and install the small set of dependencies Forgejo itself needs — git, curl, and a few SSL utilities.

    bash
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y git curl ca-certificates gnupg lsb-release

    Verify git is at least 2.0 (Ubuntu 24.04 ships 2.43):

    bash
    git --version

    Expected output:

    text
    git version 2.43.0

    If the kernel was upgraded, reboot before proceeding:

    bash
    sudo reboot

    Step 2: Create the git System User

    Forgejo runs as an unprivileged git system user. This is also the user that owns repositories on disk and accepts SSH push/pull via the git@ address. Create it with a locked password, a home directory, and the standard /bin/bash shell so the built-in SSH shim works.

    bash
    sudo adduser \
       --system \
       --shell /bin/bash \
       --gecos 'Forgejo Git Service' \
       --group \
       --disabled-password \
       --home /home/git \
       git

    Expected output:

    text
    Adding system user git' (UID 112) ...
    Adding new group git' (GID 119) ...
    Adding new user git' (UID 112) with group git' ...
    Creating home directory /home/git' ...</code></pre></div>

    Step 3: Install PostgreSQL and Create the Database

    Forgejo supports SQLite, MySQL, and PostgreSQL. SQLite is fine for personal use but PostgreSQL is the right choice for any team — it handles concurrent writes, pull request notifications, and the Actions queue far better.

    Install PostgreSQL 16 (the default on 24.04):

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo apt install -y postgresql postgresql-contrib sudo systemctl enable --now postgresql</code></pre></div>

    Create a dedicated role and database. Generate a strong password first and record it somewhere safe — you will paste it into app.ini and the web installer.

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo -u postgres psql &lt;&lt;'EOF' CREATE ROLE forgejo WITH LOGIN PASSWORD 'REPLACE_WITH_A_LONG_RANDOM_PASSWORD'; CREATE DATABASE forgejo WITH OWNER forgejo ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0; \q EOF</code></pre></div>

    Confirm the database is reachable from the git user:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo -u git psql -h 127.0.0.1 -U forgejo -d forgejo -c '\conninfo'</code></pre></div>

    You will be prompted for the password. On success:

    <div class="code-block" data-lang="text"><div class="code-block__header"><span class="code-block__lang">text</span></div><pre><code class="language-text">You are connected to database &quot;forgejo&quot; as user &quot;forgejo&quot; on host &quot;127.0.0.1&quot; at port &quot;5432&quot;.</code></pre></div>

    Step 4: Download the Forgejo Binary

    Forgejo ships as a single static Go binary. There is no PPA or official .deb — you download, verify, and drop it into /usr/local/bin.

    Set the version you want to install. Check forgejo.org/releases and pin to the current stable:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">FORGEJO_VERSION=10.0.0 sudo curl -fsSL -o /usr/local/bin/forgejo \ &quot;https://codeberg.org/forgejo/forgejo/releases/download/v${FORGEJO_VERSION}/forgejo-${FORGEJO_VERSION}-linux-amd64&quot; sudo chmod +x /usr/local/bin/forgejo</code></pre></div>

    Verify the binary runs:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">forgejo --version</code></pre></div>

    Expected output:

    <div class="code-block" data-lang="text"><div class="code-block__header"><span class="code-block__lang">text</span></div><pre><code class="language-text">Forgejo version 10.0.0+gitea-1.22.0 built with GNU Make 4.4.1, go1.23.2 : bindata, sqlite, sqlite_unlock_notify</code></pre></div>

    For production deployments you should also verify the GPG signature of the release. The Forgejo release key and instructions live at forgejo.org/docs/latest/admin/signing.

    Step 5: Prepare Directories and app.ini

    Create the directory layout Forgejo expects. Everything data-related lives under /var/lib/forgejo, configuration under /etc/forgejo, and logs under /var/log/forgejo.

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo mkdir -p /var/lib/forgejo/{custom,data,log} sudo chown -R git:git /var/lib/forgejo/ sudo chmod -R 750 /var/lib/forgejo/

    sudo mkdir -p /etc/forgejo sudo chown root:git /etc/forgejo sudo chmod 770 /etc/forgejo</code></pre></div>

    Create a minimal app.ini. The web wizard in Step 7 will fill in the rest, but seeding the file with the right ownership avoids permission problems.

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo touch /etc/forgejo/app.ini sudo chown root:git /etc/forgejo/app.ini sudo chmod 660 /etc/forgejo/app.ini</code></pre></div>

    Step 6: Install the systemd Service

    Drop in the systemd unit. Forgejo publishes a reference unit; the one below is adapted for Ubuntu 24.04 path conventions.

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo tee /etc/systemd/system/forgejo.service &gt; /dev/null &lt;&lt;'EOF' [Unit] Description=Forgejo (Self-hosted Git service) After=network.target postgresql.service Wants=postgresql.service

    [Service] Type=simple User=git Group=git WorkingDirectory=/var/lib/forgejo/ ExecStart=/usr/local/bin/forgejo web --config /etc/forgejo/app.ini Restart=always RestartSec=2s Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/forgejo

    Hardening

    CapabilityBoundingSet=CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_BIND_SERVICE NoNewPrivileges=true ProtectSystem=full ProtectHome=true PrivateTmp=true PrivateDevices=true

    [Install] WantedBy=multi-user.target EOF</code></pre></div>

    Enable and start the service:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo systemctl daemon-reload sudo systemctl enable --now forgejo sudo systemctl status forgejo --no-pager</code></pre></div>

    Expected output (abbreviated):

    <div class="code-block" data-lang="text"><div class="code-block__header"><span class="code-block__lang">text</span></div><pre><code class="language-text">● forgejo.service - Forgejo (Self-hosted Git service) Loaded: loaded (/etc/systemd/system/forgejo.service; enabled) Active: active (running) Main PID: 4821 (forgejo)</code></pre></div>

    Confirm the HTTP port is bound:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">ss -tlnp | grep 3000</code></pre></div>

    Step 7: Complete the First-Run Web Wizard

    Forgejo boots into an installation wizard the first time it is reached on port 3000. Open an SSH tunnel from your workstation so the wizard is not exposed to the public internet:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">ssh -L 3000:127.0.0.1:3000 root@your-server-ip</code></pre></div>

    Now browse to http://localhost:3000 on your laptop. Fill in the form:

    • Database Type: PostgreSQL
    • Host: 127.0.0.1:5432
    • Username: forgejo
    • Password: the password from Step 3
    • Database Name: forgejo
    • SSL: Disable (we are on localhost)
    • Server Domain: git.example.com (your real domain)
    • SSH Server Port: 22
    • Forgejo HTTP Listen Port: 3000
    • Forgejo Base URL: https://git.example.com/
    • Log Path: /var/lib/forgejo/log
    Expand Server and Third-Party Service Settings and toggle:

    • Disable Self-Registration: ON (for private forges)
    • Enable Local Mode Sign-In: ON
    Expand Administrator Account Settings and create your first admin user — this is the account that will own everything until you add more users.

    Click Install Forgejo. The wizard writes /etc/forgejo/app.ini, runs the initial schema migrations in PostgreSQL, and redirects you to the freshly seeded instance.

    Tighten the config file now that it has content:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo chmod 640 /etc/forgejo/app.ini</code></pre></div>

    Step 8: Create Your First Repository

    Still in the tunnelled browser session, click + → New Repository, give it a name (e.g. hello-forgejo), and pick "Initialise Repository".

    Back on your local machine, clone over HTTPS to confirm round-trip works:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">git clone https://git.example.com/yourusername/hello-forgejo.git cd hello-forgejo echo &quot;# Hello Forgejo&quot; &gt; README.md git add README.md git commit -m &quot;First commit&quot; git push origin main</code></pre></div>

    For SSH access, add your public key under Settings → SSH / GPG Keys in the web UI, then clone with the git@ form:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">git clone [email protected]:yourusername/hello-forgejo.git</code></pre></div>

    Step 9: Install the Forgejo Actions CI Runner

    Forgejo Actions runs workflows in the same YAML dialect as GitHub Actions, but it needs a runner — a separate agent that polls Forgejo for queued jobs and executes them. You can run the runner on the same VPS for small workloads or on dedicated nodes for production.

    First, enable Actions in app.ini:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo tee -a /etc/forgejo/app.ini &gt; /dev/null &lt;&lt;'EOF'

    [actions] ENABLED = true DEFAULT_ACTIONS_URL = https://code.forgejo.org EOF

    sudo systemctl restart forgejo</code></pre></div>

    Download the runner binary:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">RUNNER_VERSION=6.0.0 sudo curl -fsSL -o /usr/local/bin/forgejo-runner \ &quot;https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-amd64&quot; sudo chmod +x /usr/local/bin/forgejo-runner</code></pre></div>

    Register the runner against your Forgejo instance. In the web UI go to Site Administration → Actions → Runners → Create new Runner and copy the registration token.

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo mkdir -p /var/lib/forgejo-runner sudo chown git:git /var/lib/forgejo-runner cd /var/lib/forgejo-runner

    sudo -u git forgejo-runner register \ --no-interactive \ --instance https://git.example.com \ --token YOUR_REGISTRATION_TOKEN \ --name &quot;$(hostname)-runner&quot; \ --labels &quot;ubuntu-24.04:docker://ghcr.io/catthehacker/ubuntu:act-24.04&quot;</code></pre></div>

    This creates .runner in the working directory. Now install Docker so the runner can spawn container-based workflows:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo apt install -y docker.io sudo usermod -aG docker git</code></pre></div>

    Create a systemd unit for the runner:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo tee /etc/systemd/system/forgejo-runner.service &gt; /dev/null &lt;&lt;'EOF' [Unit] Description=Forgejo Actions Runner After=forgejo.service docker.service Requires=docker.service

    [Service] Type=simple User=git Group=git WorkingDirectory=/var/lib/forgejo-runner ExecStart=/usr/local/bin/forgejo-runner daemon Restart=always RestartSec=5s

    [Install] WantedBy=multi-user.target EOF

    sudo systemctl daemon-reload sudo systemctl enable --now forgejo-runner</code></pre></div>

    Confirm the runner connected:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo systemctl status forgejo-runner --no-pager</code></pre></div>

    Drop a smoke-test workflow into your repo at .forgejo/workflows/ci.yml:

    <div class="code-block" data-lang="yaml"><div class="code-block__header"><span class="code-block__lang">yaml</span></div><pre><code class="language-yaml">on: [push] jobs: hello: runs-on: ubuntu-24.04 steps: - run: echo &quot;Hello from Forgejo Actions&quot;</code></pre></div>

    Push, and watch the run appear under Actions in the web UI.

    Step 10: Enable ActivityPub Federation (Beta)

    Federation lets users on different Forgejo (and eventually other ActivityPub) instances follow each other, star repositories across servers, and — as the feature matures — collaborate on issues and pull requests without centralised accounts. As of 2026 it is beta: stable enough for internal and community forges, not yet ready for business-critical cross-instance workflows.

    Enable it by appending to app.ini:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo tee -a /etc/forgejo/app.ini &gt; /dev/null &lt;&lt;'EOF'

    [federation] ENABLED = true SHARE_USER_STATISTICS = true MAX_SIZE = 4 ALGORITHMS = rsa-sha256, rsa-sha512, ed25519 DIGEST_ALGORITHM = SHA-256 GET_HEADERS = (request-target), Date POST_HEADERS = (request-target), Date, Digest EOF

    sudo systemctl restart forgejo</code></pre></div>

    Verify the ActivityPub actor document is served:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">curl -s -H &quot;Accept: application/activity+json&quot; \ https://git.example.com/api/v1/activitypub/user-id/1 | head -20</code></pre></div>

    You should see a JSON-LD document with @context, type: Person, and public key material.

    You can now paste your profile URL into any Fediverse client that understands ActivityPub actors. Cross-instance starring between Forgejo servers works today; full PR federation is tracked in the Forgejo federation roadmap.

    Step 11: Nginx Reverse Proxy with TLS

    Running Forgejo directly on port 3000 is fine for testing but you want Nginx in front of it for TLS, HTTP/2, large file uploads, and proper access logging.

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo apt install -y nginx certbot python3-certbot-nginx</code></pre></div>

    Create the site config:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo tee /etc/nginx/sites-available/forgejo &gt; /dev/null &lt;&lt;'EOF' server { listen 80; server_name git.example.com; return 301 https://$host$request_uri; }

    server { listen 443 ssl http2; server_name git.example.com;

    # Large uploads (git pushes, package releases) client_max_body_size 1024m;

    # Security headers add_header X-Content-Type-Options nosniff; add_header X-Frame-Options SAMEORIGIN; add_header Referrer-Policy strict-origin-when-cross-origin;

    location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;

    # WebSocket + streaming (Actions logs, long polls) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection &quot;upgrade&quot;; proxy_buffering off; proxy_read_timeout 600s; } } EOF

    sudo ln -s /etc/nginx/sites-available/forgejo /etc/nginx/sites-enabled/ sudo rm -f /etc/nginx/sites-enabled/default sudo nginx -t</code></pre></div>

    Obtain a certificate:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo certbot --nginx -d git.example.com</code></pre></div>

    Update app.ini so Forgejo emits the correct external URLs:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo sed -i \ -e 's|^ROOT_URL =.|ROOT_URL = https://git.example.com/|' \ -e 's|^PROTOCOL =.|PROTOCOL = http|' \ /etc/forgejo/app.ini

    sudo systemctl restart forgejo sudo systemctl reload nginx</code></pre></div>

    Lock the firewall down:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw --force enable</code></pre></div>

    Browse to https://git.example.com — you now have a production-shaped Forgejo instance with HTTPS, HTTP/2, federation, and Actions.

    Troubleshooting

    <div class="article-table-wrap"><table><thead><tr><th>Problem</th><th>Cause</th><th>Solution</th></tr></thead><tbody><tr><td><code>502 Bad Gateway</code> from Nginx</td><td>Forgejo not listening or crashed</td><td><code>sudo systemctl status forgejo</code>, check <code>journalctl -u forgejo -n 100</code></td></tr><tr><td><code>permission denied</code> writing to <code>/var/lib/forgejo</code></td><td>Ownership drifted</td><td><code>sudo chown -R git:git /var/lib/forgejo</code></td></tr><tr><td>Push over SSH fails with <code>Permission denied (publickey)</code></td><td>Key not added or wrong user</td><td>Add key under Settings → SSH Keys; confirm you are cloning as <code>git@host</code> not <code>root@host</code></td></tr><tr><td>Actions runner shows "offline"</td><td>Registration token expired or bad labels</td><td>Re-run <code>forgejo-runner register</code> with a fresh token</td></tr><tr><td>Large pushes rejected with <code>413</code></td><td>Nginx <code>client_max_body_size</code> too low</td><td>Raise to <code>1024m</code> or higher, reload Nginx</td></tr><tr><td>Federation actor returns HTML</td><td><code>[federation] ENABLED = false</code></td><td>Set true in <code>app.ini</code> and restart</td></tr><tr><td>Database migrations fail on upgrade</td><td>Skipped versions</td><td>Always upgrade one minor version at a time, snapshot DB first</td></tr></tbody></table></div> Tail live logs to diagnose anything else:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo journalctl -u forgejo -f sudo journalctl -u forgejo-runner -f</code></pre></div>

    FAQ

    What is the difference between Forgejo and Gitea?

    Forgejo is a hard fork of Gitea created in late 2022 after Gitea's codebase was transferred to a for-profit company. Forgejo is governed by the non-profit Codeberg e.V. and is developed entirely by the community under a copyleft GPLv3 license. Functionally the two are nearly identical for day-to-day use, but Forgejo prioritises community governance, ActivityPub federation, and accessibility investment.

    Can I migrate my existing Gitea instance to Forgejo?

    Yes. Forgejo maintains binary compatibility with Gitea up to certain versions. The migration path is: stop Gitea, back up the data directory and database, swap the binary for Forgejo of a compatible version, and start the service. The database schema migrates automatically on first boot. Always snapshot the database and the repositories directory before switching, and read the specific migration notes for your source Gitea version on forgejo.org/docs.

    Does Forgejo support GitHub Actions style CI?

    Yes. Forgejo Actions uses the same YAML syntax and largely the same actions/* ecosystem as GitHub Actions. Workflows live in .forgejo/workflows or .github/workflows and are executed by the Forgejo runner, which you install as a separate agent. Most public GitHub Actions work unchanged; a few that call the GitHub API specifically need adaptation.

    Is ActivityPub federation production-ready?

    Federation in Forgejo is labelled experimental as of 2026. Basic actor discovery, follow, and star federation work between Forgejo instances today. Full cross-instance pull requests and issues are still in development. The recommendation is to enable it on internal or community projects first, watch the release notes, and broaden scope as stability lands.

    How much RAM does Forgejo need?

    Forgejo itself is extremely lightweight — roughly 150 to 300 MB of RAM at idle for a small team. Adding PostgreSQL on the same box pushes the realistic minimum to about 1 GB. If you co-locate a Forgejo Actions runner executing Docker-based workflows, 4 GB is comfortable. Heavy CI usage or large monorepos benefit from 8 GB and SSD storage.

    Can Forgejo replace GitHub Enterprise?

    For most engineering teams yes. Forgejo covers repositories, pull requests with reviews, issues, wikis, packages (container, npm, PyPI, Maven, and more), releases, Actions CI, and SSO via OAuth2/OIDC/LDAP/SAML. Large enterprises requiring GitHub Advanced Security features like CodeQL scanning may need to pair Forgejo with separate tools such as Trivy, Semgrep, and Dependabot alternatives. The cost delta usually justifies the integration effort.

    Do I need a separate CI server?

    No — the Forgejo Actions runner can be installed on the same VPS for small workloads and is how most teams start. For heavier pipelines, better isolation, or parallel execution, run runners on dedicated VPS nodes or inside Docker containers and register them against your Forgejo instance. Runners are stateless and can be added or removed at any time from the admin UI.

    Next Steps

    • Back up on a schedule. Run forgejo dump nightly as the git user and ship the archive to off-site storage. The dump includes PostgreSQL, repositories, attachments, and config.
    • Wire up SSO. If you already run Authentik, Keycloak, or Google Workspace, configure Forgejo as an OIDC client under Site Administration → Authentication Sources and disable local registration.
    • Add package registries to CI. Push container images to Forgejo's built-in registry from your Actions workflows — no external Docker Hub required.
    • Compare alternatives in our Gitea install guide, GitLab install guide, and Drone CI install guide if you want to benchmark your options.
    • Read the docs. The canonical reference for every app.ini` flag and every Actions expression is forgejo.org/docs. Bookmark it.

    Ready to host your own Git forge?
    >
    Forgejo runs comfortably on our Starter VPS with room for a team of 20 and a co-located CI runner. Deploy Ubuntu 24.04 in 60 seconds and follow this guide end to end.
    >
    - 2 vCPU, 4 GB RAM, 50 GB NVMe
    - Unmetered bandwidth
    - Full root access and snapshot backups
    >
    Order a Starter VPS and own your source code.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket