Self-Hosted GitHub

Self-Hosted GitHub: Build Your Own Git Server with Gitea on Debian 13 (Trixie)

There is a persistent myth in the developer world that running your own Git server with a full web interface, pull requests, issue trackers, and multi-user SSH support is some massive enterprise undertaking. People imagine complex containers, heavy database clusters, and hours of fighting Linux permissions.

In reality, it takes about 30 minutes – and you can easily finish the whole thing with one hand while eating a sandwich. Maybe not on the first try (because there is some reading to do), but the moral of the story is that it isn’t rocket science.

The secret weapon is Gitea. Written in Go, Gitea is distributed as a single executable, runs happily on SQLite, and provides a surprisingly complete development platform without requiring a database server or an army of dependencies.

Even better, it handles multiple users seamlessly over SSH keys without giving those users shell access to your Linux server.

In this guide, we’ll turn a fresh Debian 13 (Trixie) installation into a self-hosted GitHub alternative using:

  • Gitea
  • Git
  • SQLite3
  • OpenSSH
  • Nginx
  • HTTPS

We’ll cover two installation methods: installing Gitea through a third-party APT repository or downloading the official binary directly.

An important design decision: Regardless of which installation method you choose, we’ll use one dedicated Linux account named gitea to run the application and handle Git-over-SSH connections. This keeps the rest of the configuration consistent.

Index

  1. Understanding Linux users and SSH authentication
  2. Installing the prerequisites
  3. Installing Gitea (two methods)
  4. Understanding Gitea’s directories and permissions
  5. Configuring systemd
  6. Configuring Nginx and HTTPS
  7. Completing the web setup wizard
  8. Generating SSH keys and pushing code
  9. Adding users and creating repositories
  10. Backups and maintenance
  11. Troubleshooting

1. Wait – How Can Users Push Over SSH Without Having Linux Accounts?

Before we touch the terminal, let’s clear up the single biggest point of confusion for anyone setting up a Git server for the first time.

When you add ten friends or coworkers to your Gitea server, you do not create ten Linux users on Debian.

If you did that, they could potentially log into your server’s terminal, explore its filesystem, and generally make a mess.

Instead, we create one dedicated Linux system account named gitea. Every person who uses your server connects through that same account:

gitea@git.example.com:alice/my-project.git

Wait a minute. If everyone connects as gitea, how does the server know who is Alice and who is Bob? And what stops Bob from obtaining a command prompt on the server?

The magic of authorized_keys command interception

When Alice logs into the Gitea web interface and adds her public SSH key to her account settings, Gitea adds that key to the Linux account’s SSH authorized_keys file.

Importantly, Gitea doesn’t simply paste the raw public key. It associates the key with a special OpenSSH rule that looks approximately like this:

command="/usr/local/bin/gitea serv key-1 --config=/etc/gitea/app.ini",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... alice@laptop

The actual executable path and key identifier will depend on your installation.

Here’s what happens when Alice runs git push:

  1. Identification: OpenSSH checks the incoming key against authorized_keys and identifies it as key-1, which belongs to Alice.
  2. Interception: OpenSSH sees the command="..." restriction. Instead of executing whatever command Alice requested, it launches Gitea’s internal serv command.
  3. Lockdown: Options such as no-pty and no-port-forwarding prevent the SSH connection from being used as an ordinary interactive terminal or network tunnel.
  4. Authorization: Gitea checks its database, identifies Alice, verifies her permissions on the requested repository, and processes the Git operation.

If Alice tries to get clever and runs:

ssh -T gitea@git.example.com

She won’t receive a Linux shell. Instead, Gitea responds with something similar to:

Hi there, alice! You've successfully authenticated with the key named alice@laptop, but Gitea does not provide shell access.

And kicks her right out politely shows her the door.

Fast, native SSH speed, with no ordinary shell access.

A note about the Linux account: The name gitea is simply our chosen operating-system username. It is not a Gitea application username, nor is it the Git executable. Git is a separate program installed on the server and on developers’ computers.

Gitea uses Git to perform repository operations, while its own database manages application users, permissions, issues, and repositories.


2. Install the Basics on Debian 13 (Trixie)

Log into your Debian 13 virtual machine as root or a user with sudo privileges.

Install the core packages:

sudo apt update && sudo apt upgrade -y

sudo apt install -y \
    git \
    sqlite3 \
    nginx \
    certbot \
    python3-certbot-nginx \
    wget \
    curl \
    ca-certificates

Verify that Git is installed properly:

git --version

Gitea requires Git to be installed on the server because it delegates actual version-control operations to the Git executable.


3. Install Gitea

There are two convenient ways to install Gitea:

  • Method 1: Install it through a third-party APT repository, allowing Debian’s package manager to handle updates.
  • Method 2: Download the official precompiled binary and manage updates yourself.

Choose ONE method. Do not execute both.

Gitea is not currently packaged in Debian’s official repositories. The Debian Wiki provides information about the situation and points readers toward upstream installation methods:

https://wiki.debian.org/Gitea

The official open-source project is:

https://github.com/go-gitea/gitea

Gitea is released under the MIT license, making it a very permissively licensed piece of software.

Method 1: Install using APT

We’ll use the community-maintained Debian/Ubuntu packaging repository maintained by morph027.

This is a third-party repository, not an official Debian repository. As with any third-party package source, you’re trusting its maintainer to distribute the software and updates.

First, add the repository’s signing key:

sudo curl -fsSL \
    -o /etc/apt/trusted.gpg.d/morph027-gitea.asc \
    https://packaging.gitlab.io/gitea/gpg.key

Add the repository:

echo "deb [arch=$(dpkg --print-architecture)] https://packaging.gitlab.io/gitea gitea main" \
    | sudo tee /etc/apt/sources.list.d/morph027-gitea.list

Update the package index and install Gitea together with the repository’s keyring package:

sudo apt update

sudo apt install -y gitea morph027-keyring

Verify the installation:

which gitea

gitea --version

The executable should normally be located at:

/usr/bin/gitea

The package also creates its own Linux service account and installs a systemd service.

Check that the account exists:

getent passwd gitea

And inspect the packaged service:

systemctl cat gitea

That’s it for the installation. The package provides the service and its initial directory structure, so we won’t replace its systemd unit with our own.

You can proceed to Step 4.

Repository instructions and updates:

https://gitlab.com/packaging/gitea

Method 2: Download the official binary

Because Gitea is written in Go, we don’t need to install dozens of runtime libraries. We simply download the precompiled executable and put it in /usr/local/bin.

Visit the official downloads page:

https://dl.gitea.com/gitea

Choose the latest stable release and the appropriate Linux architecture.

For example, for a 64-bit Intel/AMD machine:

GITEA_VERSION="REPLACE_WITH_LATEST_VERSION"

Download the binary:

sudo wget -O /usr/local/bin/gitea \
    "https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64"

Make it executable:

sudo chmod 755 /usr/local/bin/gitea

Test it:

/usr/local/bin/gitea --version

For security, verify the downloaded binary against the checksum or signature published for that release before using it.

Create the Gitea Linux account

Unlike the APT method, the binary installation does not automatically create the Linux account for us.

We’ll create the same gitea account used by the APT installation.

sudo adduser \
    --system \
    --shell /bin/bash \
    --gecos 'Gitea Service' \
    --group \
    --disabled-password \
    --home /var/lib/gitea \
    gitea

A few notes about these options:

  • --system: Creates a dedicated system account.
  • --shell /bin/bash: Provides a valid shell for OpenSSH to execute Gitea’s forced commands. It does not mean users receive unrestricted shell access.
  • --group: Creates a matching gitea group.
  • --disabled-password: Prevents password-based login to this account.
  • --home /var/lib/gitea: Establishes the service account’s home directory.

We deliberately use /var/lib/gitea as the home directory so that the binary installation has a consistent working and data location.

Continue to Step 4.


4. Set Up Gitea’s Folders and Permissions

Gitea needs somewhere to store its SQLite database, Git repositories, logs, attachments, and configuration.

We’ll use a conventional Linux directory layout:

DirectoryPurpose
/var/lib/giteaApplication working directory and service account home (binary installation)
/var/lib/gitea/dataDatabase, repositories and application data
/var/lib/gitea/customCustom templates, configuration and other custom resources
/var/lib/gitea/logApplication logs
/etc/giteaGitea configuration, including app.ini
/usr/local/bin/giteaBinary installation executable

The APT installation creates its own directory structure. Inspect it rather than blindly overwriting it.

For the binary installation, create the directories:

sudo mkdir -p /var/lib/gitea/{custom,data,log}

sudo chown -R gitea:gitea /var/lib/gitea

sudo chmod -R 750 /var/lib/gitea

Create the configuration directory:

sudo mkdir -p /etc/gitea

sudo chown root:gitea /etc/gitea

sudo chmod 770 /etc/gitea

Why 770?

During the initial web setup wizard, Gitea needs permission to create its app.ini configuration file.

We temporarily allow the service account to write to the configuration directory. Once the wizard is finished, we’ll lock it down.

For the APT installation, inspect the existing permissions and ownership before changing anything:

sudo ls -ld /var/lib/gitea /etc/gitea

The packaged configuration and data directories should already be prepared for the gitea account.

A note about SSH directories

Gitea normally stores its OpenSSH authorized_keys file in the .ssh directory belonging to its runtime account.

For the binary installation, this will normally be:

/var/lib/gitea/.ssh/authorized_keys

For the APT installation, use the home directory reported by:

getent passwd gitea

This is important because OpenSSH and Gitea must agree on where that file lives.

We will leave Gitea’s default SSH_ROOT_PATH behavior intact rather than hardcoding a different SSH directory.


5. Configure the Systemd Service

We want Gitea to start automatically whenever Debian boots and restart itself if it crashes.

The two installation methods already share the same service identity, but their service installation differs.

If you installed through APT

The package already provides a systemd service.

Enable and start it:

sudo systemctl enable --now gitea

Inspect it:

sudo systemctl status gitea

Don’t replace the package-provided service file unless you have a specific reason to do so.

If you installed the binary

We need to create the systemd service ourselves.

Open a new service file:

sudo nano /etc/systemd/system/gitea.service

Paste the following:

[Unit]
Description=Gitea (Git with a cup of tea)
After=network.target

[Service]
RestartSec=2s
Type=simple

User=gitea
Group=gitea

WorkingDirectory=/var/lib/gitea

ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini

Restart=always

Environment=USER=gitea HOME=/var/lib/gitea GITEA_WORK_DIR=/var/lib/gitea

[Install]
WantedBy=multi-user.target

Save and exit Nano (Ctrl+O, Enter, then Ctrl+X).

Reload systemd and start Gitea:

sudo systemctl daemon-reload

sudo systemctl enable --now gitea

Check that the service is running:

sudo systemctl status gitea

You should see:

Active: active (running)

Gitea should now be listening on port 3000.

Check the runtime identity

Regardless of installation method, Gitea should run as the gitea operating-system account.

The application configuration should also identify that account as its runtime user:

[DEFAULT]
RUN_USER = gitea

The packaged installation should already provide the appropriate setting. For the binary installation, Gitea normally derives it from the environment, but explicitly verifying it is worthwhile.

You can inspect the configuration after it has been created in the next steps.


6. Put Nginx in Front for HTTPS (Port 443)

Connecting to http://your-ip:3000 works in a home test lab, but typing :3000 gets old fast, and sending passwords over unencrypted HTTP is a bad habit.

Let’s configure Nginx as a reverse proxy.

Visitors will connect over standard HTTPS on port 443, and Nginx will quietly forward the traffic to Gitea on 127.0.0.1:3000.

First, decide what hostname you’ll use.

For example:

git.example.com

Or, for a private LAN:

gitea.home.arpa

Replace the examples throughout this section with your actual hostname.

Create the Nginx site configuration

Create the configuration file:

sudo nano /etc/nginx/sites-available/gitea

Paste:

server {
    listen 80;
    listen [::]:80;

    server_name git.example.com;

    client_max_body_size 512M;

    location / {
        proxy_pass http://127.0.0.1:3000;

        proxy_http_version 1.1;

        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;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;
    }
}

The client_max_body_size setting allows large HTTP uploads. It is not a limit on Git-over-SSH pushes, which bypass Nginx.

The WebSocket headers also allow Gitea’s real-time browser features to work correctly.

Enable the site:

sudo ln -s \
    /etc/nginx/sites-available/gitea \
    /etc/nginx/sites-enabled/gitea

Test the configuration:

sudo nginx -t

Reload Nginx:

sudo systemctl reload nginx

Adding HTTPS: Choose the appropriate option

There are two common scenarios.

Option A: Your server has a public domain name

If git.example.com resolves to your server’s public IP address and the server is reachable from the Internet, Certbot can obtain a free Let’s Encrypt certificate.

Make sure:

  • DNS points to your server.
  • TCP ports 80 and 443 are reachable.
  • If you’re behind a router, the required ports are forwarded to your VM.

Run:

sudo certbot --nginx -d git.example.com

Follow the instructions.

Certbot will obtain the certificate and configure Nginx for HTTPS.

You should now be able to access:

https://git.example.com

Option B: Your server is strictly on a private LAN

Suppose your VM is at:

192.168.1.50

And you want to access it using:

gitea.home.arpa

You don’t need to expose the server to the Internet or purchase a domain.

You do, however, need to configure local DNS (or a hosts-file entry) so that gitea.home.arpa resolves to 192.168.1.50.

For HTTPS, I recommend using a locally trusted certificate authority rather than relying on an untrusted self-signed certificate.

One convenient tool is mkcert:

https://github.com/FiloSottile/mkcert

On a trusted administration workstation, install mkcert and initialize its local CA:

mkcert -install

Generate a certificate containing the hostname and IP address:

mkcert \
    -cert-file gitea.pem \
    -key-file gitea-key.pem \
    gitea.home.arpa \
    192.168.1.50

Copy the generated certificate and private key to your Debian server.

For example:

/etc/nginx/ssl/gitea.pem
/etc/nginx/ssl/gitea-key.pem

Protect the private key:

sudo chown root:root /etc/nginx/ssl/gitea-key.pem

sudo chmod 600 /etc/nginx/ssl/gitea-key.pem

You must also distribute the mkcert root CA certificate to the computers that will access Gitea and install it into their trusted certificate stores.

You can locate the CA directory on the workstation with:

mkcert -CAROOT

Copy rootCA.pem to your clients, not rootCA-key.pem. Never distribute the CA private key.

Now replace the Nginx configuration with:

server {
    listen 80;
    listen [::]:80;

    server_name gitea.home.arpa;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name gitea.home.arpa;

    ssl_certificate /etc/nginx/ssl/gitea.pem;
    ssl_certificate_key /etc/nginx/ssl/gitea-key.pem;

    client_max_body_size 512M;

    location / {
        proxy_pass http://127.0.0.1:3000;

        proxy_http_version 1.1;

        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;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;
    }
}

Test and reload:

sudo nginx -t

sudo systemctl reload nginx

You can now access Gitea over HTTPS on your LAN without browser certificate warnings, provided the client trusts your local CA.

A final note: A self-signed certificate is also possible, but encryption and certificate trust are two different things. A self-signed certificate encrypts traffic, but browsers and Git clients will still complain until you explicitly trust it.


7. Complete the Web Setup Wizard

Open your browser and visit:

https://git.example.com

Or your private LAN hostname:

https://gitea.home.arpa

You’ll be greeted by Gitea’s initial configuration page.

Because we’re using SQLite and standard OpenSSH, most defaults are already suitable.

1. Database settings

  • Database Type: SQLite3.
  • Database Path: /var/lib/gitea/data/gitea.db.

This gives us a simple database file instead of requiring a separate database server.

2. General settings

  • Site Title: Choose whatever you like.
  • Server Domain: Your actual hostname.
  • SSH Server Domain: Your actual hostname.
  • SSH Server Port: 22.
  • Gitea HTTP Listen Port: 3000.
  • Gitea Base URL: https://git.example.com/.

For a LAN installation, use your LAN hostname instead.

3. Optional settings

Expand the Server and Third-Party Service Settings section.

I recommend disabling self-registration if this is a private server where you want to control who can create accounts.

You can always enable it later.

4. Administrator account

Expand Administrator Account Settings and create your administrator account.

Use a strong password and enable multi-factor authentication after installation.

Click Install Gitea.

Within a few seconds, your dashboard should load.

Lock down the configuration file

Now that Gitea has created /etc/gitea/app.ini, let’s make a few important adjustments.

Open:

sudo nano /etc/gitea/app.ini

Find the [server] section and make sure it contains the appropriate settings:

[server]
DOMAIN = git.example.com
ROOT_URL = https://git.example.com/

HTTP_ADDR = 127.0.0.1
HTTP_PORT = 3000

SSH_DOMAIN = git.example.com
SSH_PORT = 22

Replace the example domain with your actual hostname.

The HTTP_ADDR setting ensures Gitea only listens on localhost for HTTP requests. Visitors must go through Nginx rather than accessing port 3000 directly.

Also verify that the default runtime user is correct:

[DEFAULT]
RUN_USER = gitea

For the SSH integration, the displayed SSH username should also be gitea. Gitea normally derives this from its runtime account, but verify the generated clone URL rather than assuming.

Important: Do not change the APT package’s service configuration or SSH paths blindly. The package may provide additional defaults. We want to preserve its working directory and authorized-keys arrangement.

Lock down the configuration directory

The configuration file contains sensitive information, including database and application secrets.

For the binary installation, and for an APT installation where the ownership matches this layout, run:

sudo chown root:gitea /etc/gitea

sudo chmod 750 /etc/gitea

sudo chown root:gitea /etc/gitea/app.ini

sudo chmod 640 /etc/gitea/app.ini

The gitea service account can read the configuration through its group membership, but cannot modify it.

Restart Gitea:

sudo systemctl restart gitea

8. Generate Your SSH Key and Push Code!

Why use SSH keys instead of typing a username and password every time you run git push?

  1. Convenience: You don’t need to enter your credentials for every operation.
  2. Security: Public-key authentication avoids transmitting a reusable password during Git operations.
  3. Access control: You can register multiple keys and revoke individual keys without changing your account password.

1. Generate a keypair on your workstation

Important: Run these commands on your personal computer where you write code, NOT inside the Debian Git server VM.

Open your terminal and run:

ssh-keygen -t ed25519 -C "your-email@example.com"

When asked where to save the key, press Enter to accept the default:

~/.ssh/id_ed25519

When asked for a passphrase, I recommend setting one, particularly if you use a laptop or shared workstation. An SSH agent can remember it during your working session.

This creates two files:

id_ed25519       Private key
id_ed25519.pub   Public key

Never share your private key.

Print your public key:

cat ~/.ssh/id_ed25519.pub

Copy the entire line.

2. Add the key to Gitea

In the Gitea web interface:

  1. Click your profile avatar in the top-right corner.
  2. Select Settings.
  3. Open the SSH / GPG Keys section.
  4. Click Add Key.
  5. Paste your public key and give it a recognizable name.

That’s it.

3. Peek under the hood (optional fun!)

Want to see the command interception magic we talked about earlier?

On your Debian server, inspect the authorized_keys file.

For the binary installation, assuming the home directory we created:

sudo cat /var/lib/gitea/.ssh/authorized_keys

For the APT installation, first check the service account’s home directory:

getent passwd gitea

Then inspect its .ssh/authorized_keys file.

You should see your newly registered key wrapped in a Gitea command="..." restriction.

4. Test your SSH connection

From your workstation:

ssh -T gitea@git.example.com

The first time you connect, SSH will ask whether you trust the server’s host key.

Verify its fingerprint through a trusted channel before accepting it.

Once authenticated, you should receive a response similar to:

Hi there, your-username! You've successfully authenticated with the key named my-laptop, but Gitea does not provide shell access.

Congratulations! Your SSH authentication is working.

5. Configure Git on your workstation

If this is your first time using Git on this machine, configure your identity:

git config --global user.name "Your Name"

git config --global user.email "your-email@example.com"

These settings identify you in your Git commits. They are separate from your Gitea login credentials.

6. Clone a repository

Create a repository in Gitea, then use its SSH clone URL.

It should look something like:

git clone gitea@git.example.com:alice/my-project.git

You can now work normally:

cd my-project

# Make changes to your files

git add .

git commit -m "My first commit"

git push

Your code is now being pushed to your own server!


9. Adding Users and Creating Repositories

Now that your server is running, adding friends, teammates, or clients is straightforward.

Log into Gitea using your administrator account.

To create a user:

  1. Click your avatar.
  2. Go to Site Administration.
  3. Open the User Accounts section.
  4. Select Create User Account.
  5. Provide the username, email and initial password.

When they log into the web interface:

  • They can create their own repositories.
  • They can add their own SSH public keys.
  • They can clone, pull and push code.
  • They can participate in collaborative projects according to the permissions you assign.

For example, Alice can clone her repository with:

git clone gitea@git.example.com:alice/my-project.git

Bob can do exactly the same with his own account and repositories.

Both use the same Linux account, gitea, but Gitea knows which application user is connecting based on the registered SSH key.

Neither receives ordinary access to your Debian operating system.

Organizations and teams

If you’re hosting repositories for a group of developers, don’t overlook Gitea Organizations.

Organizations allow you to group repositories and manage access through teams, rather than individually assigning permissions to every repository.

This is particularly useful when several developers need access to shared projects.


10. Backups and Maintenance

A Git server is only as reliable as its backups.

Although Git repositories contain their own history, Gitea stores much more than Git data.

You should protect:

  • The SQLite database.
  • Git repositories.
  • Gitea configuration.
  • Attachments and uploaded files.
  • Git LFS objects, if enabled.
  • Custom application data.

A simple backup procedure

For a small installation, a straightforward approach is to stop Gitea briefly and archive its data and configuration directories.

Create a backup directory on a separate storage device:

sudo mkdir -p /mnt/backups/gitea

Stop the service:

sudo systemctl stop gitea

Create the archive:

sudo tar -C / -czf \
    "/mnt/backups/gitea/gitea-$(date +%F).tar.gz" \
    var/lib/gitea \
    etc/gitea

Restart Gitea:

sudo systemctl start gitea

This captures the database, repositories, attachments, and configuration in a consistent stopped-service state.

Do not rely on a backup stored on the same disk as your server. Copy your backups to another machine or storage location.

For larger installations, investigate Gitea’s built-in dump and restore functionality:

https://docs.gitea.com/administration/backup-and-restore

Updating Gitea

The update procedure depends on your installation method.

APT installation:

sudo apt update

sudo apt upgrade gitea

Binary installation:

  1. Back up your installation.
  2. Check the release notes.
  3. Download the new binary.
  4. Verify its checksum or signature.
  5. Stop Gitea.
  6. Replace /usr/local/bin/gitea.
  7. Start Gitea again.

For example:

sudo systemctl stop gitea

# Replace the binary with the verified new release.

sudo systemctl start gitea

Always check the release notes before upgrading, especially when crossing major versions.


11. Troubleshooting

A few commands will save you a lot of time when something goes wrong.

Check whether Gitea is running

sudo systemctl status gitea

Follow the application logs

sudo journalctl -u gitea -f

Check Nginx

sudo nginx -t

sudo systemctl status nginx

Check whether Gitea is listening on port 3000

sudo ss -ltnp | grep 3000

After completing the configuration, you should see it listening on 127.0.0.1:3000.

Check the Linux account

getent passwd gitea

id gitea

Check SSH authentication

ssh -vT gitea@git.example.com

The verbose output can help identify whether the problem is related to the SSH key, server fingerprint, SSH account or authentication process.

Common problems

SymptomWhat to investigate
Gitea won’t startRUN_USER, service ownership, configuration and logs
HTTP 502 from NginxGitea service status and port 3000
SSH permission deniedSSH key registration, account home and authorized_keys permissions
SSH authentication works but push failsRepository permissions and Gitea account ownership
Browser certificate warningCertificate hostname, SAN and client trust store
HTTP 413 errorNginx client_max_body_size
Gitea redirects to the wrong URLROOT_URL and reverse-proxy headers

Final Thoughts

And just like that, you have your own self-hosted GitHub alternative running on Debian 13.

No Docker containers, no Kubernetes clusters, no separate database server, and no need to pay for a hosted Git platform.

You have a proper multi-user development environment, complete with SSH authentication, repositories, issue tracking, collaboration tools and a web interface.

And the best part? You control the server, the data, the backups and the users.

You probably still have half your sandwich left.

Useful References

One thought on “Self-Hosted GitHub”

Leave a Reply

Your email address will not be published. Required fields are marked *