Gitea: Your local Git solution

Managing Ansible automation code in the cloud is convenient, but storing encrypted secrets on third-party platforms like GitHub can feel uncomfortable. Even when using Ansible Vault, the encrypted data still resides on a server you don’t control. For developers running private automation code, a self-hosted Git platform in a lightweight LXC container offers full sovereignty, security, and workflow continuity.

This guide walks through setting up a Gitea instance on AlmaLinux 9, migrating your repositories from GitHub and integrating VS Code – all while retaining the GitHub experience locally.

Why move private repositories locally

Private Ansible runbooks often contain sensitive data. Moving them locally reduces exposure to third-party platforms and provides:

  • Full data control — secrets never leave your environment
  • Compliance-friendly storage – useful if policies forbid third-party hosting
  • Freedom to use GitHub strictly for public/open-source projects

For single-user or small teams setups, Gitea is particularly well-suited. Unlike GitLab, which is resource-heavy, Gitea is lightweight and easy to maintain, while still providing the essential GitHub-like features.

Gitea vs. GitLab: Key facts & system requirements

The following table summarizes why Gitea is ideal for a solo homelab setup, showing both key facts and system considerations:

AspectGiteaGitLab CE/EE
Target UseSolo devs, homelabs, small teamsEnterprise teams, CI/CD heavy
RAM512 MB – 1 GB4–8 GB
CPU1 vCPU2–4 vCPU
Disk1–5 GB (repos + metadata)10+ GB (app, DB, logs, repos)
DatabaseSQLite / MySQL / PostgresPostgreSQL only
ComplexityLightweight, single binaryHeavy stack (Ruby, Redis, Sidekiq)
TLS HandlingBuilt-in or via reverse proxy (Nginx)Built-in, heavier configuration
UpgradeSimple by replacing the binary More involved (packages + DB migrations)
Workflow CompatibilityGit CLI, VS Code integrationGit CLI, VS Code, CI/CD pipelines

Key takeaways: Gitea offers all the Git functionality needed for private automation, requires minimal system resources, and is easier to maintain in a homelab environment. GitLab is overkill for SOHO setups and demands more RAM, CPU, and disk space.

Architecture Overview

A simple, robust architecture for local Gitea:

  • AlmaLinux 9 LXC on Proxmox – lightweight container
  • Gitea instance on git.examplecorp.io
  • Nginx reverse proxy handling HTTPS via private PKI or Let’s Encrypt
  • Ansible Runbooks in private repositories
  • Access via SSH or HTTPS from VS Code or CLI

This setup ensures full control, minimal resource usage, and a workflow equivalent to GitHub.

Creating the LXC container

Before installing Gitea, create a dedicated LXC container on your Proxmox host. Keeping it isolated makes backups, snapshots, and maintenance easier. For a SOHO setup, the following specs are more than enough:

  • Container name: git
  • OS template: AlmaLinux 9 (or Debian/Ubuntu if you prefer)
  • CPU: 1 vCPU
  • Memory: 1 GB RAM (512 MB works, but 1 GB is safer)
  • Disk: 10 GB (5 GB minimum, depending on repository size)
  • Network: Static IP, bridge to LAN, optional DNS entry git.examplecorp.io

Installing Gitea on AlmaLinux 9 with Nginx

Prepare the container

dnf update -y
dnf install -y vim-enhanced nano git wget nginx

Create dedicated Gitea user

Run Gitea under its own system user:

sudo adduser --system --shell /bin/bash --comment 'Gitea Service User' --create-home --home /home/gitea gitea

Download the Gitea binary

Visit Gitea Downloads to find the latest stable version. Replace 1.24.5 with the current release:

wget -O /tmp/gitea https://dl.gitea.com/gitea/1.24.5/gitea-1.24.5-linux-amd64

Install the binary to /opt/gitea

sudo mkdir -p /opt/gitea
sudo mv /tmp/gitea /opt/gitea/gitea
sudo chmod +x /opt/gitea/gitea
sudo chown -R gitea:gitea /opt/gitea

Set up Gitea directories

Create directories for configuration, repositories, logs, and other data:

sudo mkdir -p /opt/gitea/{custom,data,indexers,public,log}
sudo chown -R gitea:gitea /opt/gitea
sudo chmod -R 750 /opt/gitea

Create a systemd service

Create a systemd unit file:

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

Insert the following:

[Unit]
Description=Gitea
After=syslog.target
After=network.target

[Service]
User=gitea
Group=gitea
WorkingDirectory=/opt/gitea/
ExecStart=/opt/gitea/gitea web
Restart=always
Environment=USER=gitea HOME=/home/gitea GITEA_WORK_DIR=/opt/gitea

[Install]
WantedBy=multi-user.target

Save and exit.

Start and enable Gitea

Reload systemd, enable the service to start on boot, and start Gitea:

sudo systemctl daemon-reload
sudo systemctl enable --now gitea
sudo systemctl status gitea

Verifying Gitea is running

Check that Gitea is active:

sudo systemctl status gitea

Verify it’s listening on port 3000:

sudo ss -tulpen | grep 3000

Expected output shows Gitea running, e.g.:

tcp   LISTEN 0 128 0.0.0.0:3000 0.0.0.0:*  users:(("gitea",pid=1234,fd=6))

Finally, open your browser:

http://<container-ip>:3000
  • You should see the Gitea setup page.
  • For HTTPS via Nginx: https://git.examplecorp.io

Configuring Nginx as a reverse proxy for Gitea (HTTP & HTTPS)

To serve Gitea over both HTTP and HTTPS, you can set up an Nginx server block with your own SSL certificate.

1. Create Nginx server block

sudo nano /etc/nginx/conf.d/gitea.conf

Add the following:

# HTTP redirect to HTTPS
server {
    listen 80;
    server_name git.examplecorp.io;

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

# HTTPS server
server {
    listen 443 ssl;
    server_name git.examplecorp.io;

    ssl_certificate /etc/nginx/ssl/gitea.crt;
    ssl_certificate_key /etc/nginx/ssl/gitea.key;

    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;
    }
}
  • Replace /etc/nginx/ssl/gitea.crt and /etc/nginx/ssl/gitea.key with the paths to your SSL certificate and key.
  • Replace git.examplecorp.io with your hostname or container IP.

2. Test Nginx configuration

sudo nginx -t
  • Make sure there are no syntax errors.

3. Enable and start Nginx

sudo systemctl enable --now nginx
sudo systemctl status nginx
  • HTTP traffic is redirected to HTTPS.
  • Gitea is now accessible via https://git.examplecorp.io.

Choosing a database backend for Gitea

Gitea supports multiple database backends. For small, single-user installations, SQLite is sufficient, but it is not recommended for production due to limited scalability and concurrency. PostgreSQL and MariaDB/MySQL are fully supported and offer robust performance for larger setups.

In this guide, we will use PostgreSQL, which Gitea itself recommends for production environments. MariaDB/MySQL would also be fine, but PostgreSQL is preferred here for its reliability and strong feature set.

1. Prepare PostgreSQL

Install PostgreSQL (example for AlmaLinux 9):

sudo dnf install -y postgresql-server postgresql-contrib
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql

2. Configure PostgreSQL authentication

Add a dedicated Gitea user and database:

sudo -u postgres psql

Inside psql:

CREATE USER gitea WITH PASSWORD 'your_password';
CREATE DATABASE gitea OWNER gitea;
\q

Update PostgreSQL HBA configuration (/var/lib/pgsql/data/pg_hba.conf) to allow MD5 password authentication for the Gitea user:

# Allow local connections with password
local   gitea   gitea   md5
host    gitea   gitea   127.0.0.1/32   md5
host    gitea   gitea   ::1/128        md5

Reload PostgreSQL:

sudo systemctl reload postgresql

Basic configuration of Gitea

After installing Gitea and preparing PostgreSQL, the next step is the initial configuration. This covers connecting to the database, setting up email notifications, and creating an administrative account separate from regular users.

1. Access the web setup

  • Open your browser and go to:
https://git.examplecorp.io
  • You should see the Gitea setup page.

2. Database configuration

  • Database Type: PostgreSQL
  • Host: 127.0.0.1:5432
  • Database Name: gitea
  • Username: gitea
  • Password: your PostgreSQL password
  • Optional Table Prefix: leave blank unless you need it.

Gitea will test the connection and confirm it is working.

3. Administrator account

  • Create a separate admin user (different from normal users) to manage repositories, users, and system settings.
  • This ensures proper separation of administrative privileges.

4. SMTP / Outgoing email configuration

  • Enable email notifications for things like password resets, invitations, and alerts.
  • Test sending an email to ensure connectivity.

5. Final steps

  • Review all settings.
  • Click Install Gitea to initialize the system.
  • After installation, log in with the admin account and start creating regular users and repositories.

Updating Gitea

When installed under /opt/gitea, it’s best to backup the existing binary before replacing it:

  1. Get the current version and store it in a variable:
CURRENT_VERSION=$(/opt/gitea/gitea --version | awk '{print $3}')
echo "Current Gitea version: $CURRENT_VERSION"
  1. Backup the current binary:
sudo cp /opt/gitea/gitea /opt/gitea/gitea_backup_$CURRENT_VERSION
  1. Download the latest binary from Gitea Downloads, replacing <latest-version>:
wget -O /tmp/gitea https://dl.gitea.io/gitea/<latest-version>/gitea-<latest-version>-linux-amd64
  1. Stop the Gitea service:
sudo systemctl stop gitea
  1. Replace the old binary:
sudo mv /tmp/gitea /opt/gitea/gitea
sudo chmod +x /opt/gitea/gitea
sudo chown gitea:gitea /opt/gitea/gitea
  1. Start the service again:
sudo systemctl start gitea
sudo systemctl status gitea
  1. Verify the update:
/opt/gitea/gitea --version
  • The previous version binary is safely stored as /opt/gitea/gitea_backup_<old-version>.
  • All repositories and configurations remain untouched in /opt/gitea/data and /opt/gitea/custom.

Migrating a GitHub repository to Gitea

Instead of using git clone --mirror, you can migrate directly inside Gitea. This approach keeps commit history, branches, and tags, while using a secure GitHub token.

1. Create a GitHub personal access token

  1. Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
  2. Click Generate new token → Generate new token (classic)
  3. Create a Note e.g. Repository Migration Gitea
  4. Select:
    • repo → full control of private repositories (needed for migration).
  5. Copy the generated token and keep it safe.

2. Use Gitea’s migrate repository feature

  1. Log in to your Gitea instance.
  2. Navigate to New Repository → Migrate Repository.
  3. Enter the GitHub clone URL: https://github.com/username/repository.git
  4. Under Authentication, paste your GitHub token.
  5. Choose:
    • Owner → user or organization in Gitea.
    • Repository Name → target name in Gitea.
    • Check Migrate issues, pull requests, and wiki if needed.
  6. Click Migrate Repository.

3. Result

  • Gitea pulls the full repository including commits, branches, and tags.
  • Issues, pull requests, and wiki pages are migrated if selected.
  • Ansible Vault secrets remain encrypted – they are just files in Git.

Note: It is recommended to delete the GitHub personal access token right after a successful migration, unless you plan to reuse it for migrating additional repositories.

Using a Gitea personal access token with VS Code

Generate a personal access token in Gitea

  1. Log in to your Gitea instance in the browser.
  2. Go to SettingsApplicationsGenerate New Token.
  3. Give it a meaningful name (e.g., ansible-vscode).
  4. Select All (public, private, and limited)
  5. Select the required scopes (usually repository is enough for cloning and pushing).
  6. Select Read and Write access rights
  7. Click Generate Token and copy it immediately – you won’t see it again later.

Clone the repository via HTTPS with token

Open VS Code and either:

  • Option A: Use the built-in Git clone command
    1. Press Ctrl+Shift+PGit: Clone.
    2. Enter the HTTPS URL with your username and token: https://<username>:<token>@git.examplecorp.io/smarthome/home-assistant-flows.git
    3. Choose a local folder where VS Code should store the repo.
    4. Open the repo when prompted.
  • Option B: Use the integrated terminal in VS Code git clone https://<username>:<token>@git.examplecorp.io/smarthome/home-assistant-flows.git cd home-assistant-flows code .

Backup and maintenance

To ensure your Gitea instance stays safe and reliable, implement the following backup and maintenance procedures:

  • Nightly LXC Snapshots – Perform full container snapshots each night. This provides a complete backup of your Gitea environment, including repositories, databases, and configuration.
  • PostgreSQL Database Backups – Gitea uses PostgreSQL as its main database. Schedule nightly backups (e.g., at 02:00) using pg_dump:
#!/bin/bash
BACKUP_DIR="/var/backups/gitea_db"
RETENTION_DAYS=7
TIMESTAMP=$(date +%F_%H-%M-%S)
mkdir -p "$BACKUP_DIR"
pg_dump -U gitea -F c gitea > "$BACKUP_DIR/gitea_$TIMESTAMP.dump"

# Remove old backups
find "$BACKUP_DIR" -type f -name "*.dump" -mtime +$RETENTION_DAYS -delete

This ensures database backups are rotated automatically, keeping only the last 7 days by default.

  • Optional Offsite Backup – For extra security, copy LXC snapshots or database backups to an offsite location. This protects against hardware failure or site-wide issues.
  • Keep Gitea Updated – Regularly update Gitea to the latest stable version to benefit from security fixes, bug patches, and new features.

With this setup, both your Gitea environment and its database are consistently backed up, maintained, and ready for recovery if needed.

Conclusion

Running Ansible runbooks on my local Gitea instance gives me exactly what I was looking for: full control, digital sovereignty, and safe handling of secrets. The workflow feels instantly familiar – whether in VS Code or on the CLI, it’s just like working with GitHub. Nightly backups add peace of mind, and I can keep GitHub purely for public open-source projects, while Gitea takes care of everything private. This clear separation between public and private projects, along with the secure handling of secrets (even with Ansible Vault), ensures sensitive configurations stay safe at all times.

What truly excites me is how lightweight Gitea is. It barely consumes any system resources and simply runs alongside everything else – no hassle, no overhead. For private repositories, versioning sensitive configurations, or just having peace of mind in my own environment, Gitea has become the perfect fit. I’m genuinely impressed with how seamless the switch was: there’s virtually no learning curve, because Gitea works so much like GitHub.com, only on my own terms.