Gitea: A Lightweight Self-Hosted Git Server on Your VPS

The Problem: Your Repos Live on Someone Else's Servers

Most of my personal projects used to live on free Git services. While the projects were public, that was fine. Once they went private — client code, internal scripts, experiments not ready for other eyes — I found myself depending on someone else's quotas, rules, and policies. There were collaborator limits, features locked behind paid plans, and the nagging feeling that important code sat on servers I didn't own.

The fix isn't to stop using Git, but to move the server onto your own VPS. That's where Gitea comes in. Gitea is an open source Git server whose interface looks familiar if you've used any popular Git service: repo lists, issues, pull requests, wikis, and a package registry. The difference is that it runs on your server, and it's famously light — comfortable on a small VPS with 1–2 GB of RAM, far below what GitLab demands.

Below I'll walk through installing it with Docker Compose, together with a PostgreSQL database, then creating your first repo, migrating old repos, and turning on its built-in CI.

Gitea vs GitLab vs GitHub: Which One to Pick

GiteaSelf-Hosted GitLabGitHub
RAM needsLight, comfortable on 1–2 GBHeavy, needs several GBNot applicable (hosted service)
InstallationOne Compose file, done in minutesA much bigger, more complex installNo installation needed
Core featuresRepos, issues, pull requests, wiki, ActionsAll of that plus full DevOpsAll of that plus the largest ecosystem
Data controlFull, on your serverFull, on your serverOn GitHub's servers
Best forPersonal use and small teamsLarge teams needing full DevOpsPublic collaboration and open source

My rule of thumb is simple: if all you need is a place to store and manage your own code — not a corporate-grade DevOps platform — Gitea is more than enough, and your VPS won't break a sweat.

Step 1: Prepare the VPS and Docker

You need an Ubuntu VPS with Docker and the Compose plugin already installed. If you haven't set that up yet, follow our Docker Compose guide first, then come back here. Also make sure ports 3000 (web) and 222 (Git SSH) aren't already used by other services on your VPS.

Step 2: Write the Gitea + PostgreSQL Compose File

Create a project folder and the compose file inside it:

sudo mkdir -p /opt/gitea && sudo chown $USER:$USER /opt/gitea
cd /opt/gitea
nano docker-compose.yml

Fill it with the following configuration. The db service is PostgreSQL, the server service is Gitea itself:

services:
  db:
    image: postgres:16-alpine
    container_name: gitea-db
    restart: unless-stopped
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=ganti-dengan-password-kuat
      - POSTGRES_DB=gitea
    volumes:
      - ./postgres-data:/var/lib/postgresql/data

  server:
    image: gitea/gitea:1.24
    container_name: gitea
    restart: unless-stopped
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=ganti-dengan-password-kuat
    volumes:
      - ./gitea-data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "222:22"
    depends_on:
      - db

Four things that are easy to miss here:

  • The password in both places must match exactly, and don't use the example above — generate a strong random password.
  • USER_UID and USER_GID 1000 are the Gitea user's UID/GID inside the container; these match the first user on Ubuntu in most cases. If your data folders belong to a different user, adjust the numbers so the container can write to them.
  • The port mapping "222:22" means Git SSH is reached via port 222 on the host, since port 22 is usually already taken by the server's own SSH. Later, your SSH clone URLs will use port 222.
  • All data — repos, database, configuration — lives in ./gitea-data and ./postgres-data. As long as those two folders are safe, Gitea can be rebuilt at any time.

Start the stack:

docker compose up -d
docker compose ps

If both services show running, open http://IP-VPS-Anda:3000 in your browser. You'll be greeted by Gitea's installation wizard.

Step 3: Complete the Installation Wizard

On the installation page, fill in the following:

  • Database type: PostgreSQL, with host db, port 5432, database name gitea, user gitea, and the same password as in the compose file. The host name db matters here: it's the name of the database service inside the Docker Compose network.
  • Gitea base URL: enter the address you'll actually use, for example http://IP-VPS-Anda:3000 for the initial stage. If you later add a domain and HTTPS, update this value so the clone URLs and email links Gitea generates stay correct.
  • Administrator account: create your first admin account here. This account will manage users and site settings.

Click install, wait for it to finish, then log in with that admin account. Your Gitea is live.

Step 4: Create Your First Repo and Push Code

From the dashboard, click the new repo button, give it a name, and choose whether it's public or private. Once the repo is created, Gitea shows its clone URLs. From your laptop:

git clone ssh://git@IP-VPS-Anda:222/username/nama-repo.git
cd nama-repo
echo "# Proyek pertama di Gitea sendiri" > README.md
git add README.md
git commit -m "Commit pertama"
git push origin main

Note the ssh://git@IP-VPS-Anda:222/...: the user is git, and the port is 222, matching the mapping in the compose file. Before your first push, make sure your public SSH key has been added in your Gitea account settings (profile menu → SSH Keys). Without it, the server will reject your SSH connection.

Step 5: Move Old Repos with Mirroring

Moving repos from an old service doesn't mean re-downloading every commit by hand. The cleanest way is a mirror clone:

git clone --mirror https://github.com/username/repo-lama.git
cd repo-lama.git
git push --mirror ssh://git@IP-VPS-Anda:222/username/repo-lama.git

The --mirror flag copies all branches, tags, and commit history. Once the push finishes, verify in Gitea's web UI that all branches and tags are complete, and only then decide whether the old repo on the old service stays as an archive or gets deleted.

Step 6: Enable CI with Gitea Actions

Gitea has built-in CI called Gitea Actions, which works much like GitHub Actions: you write workflow YAML files in the .gitea/workflows folder inside a repo, and a runner executes them. To run it, you need one runner — a process running on a server (or another machine) that registers with your Gitea using a token from Site Administration → Actions → Runners.

For personal use, a single runner on the same VPS is plenty for light workflows like running tests or builds. One warning: the runner executes code from repos, so don't casually enable a runner for repos that people you don't trust can write to. For private repos, the risk is small and the payoff is large.

Maintaining Gitea: Backups and Updates

Backing up Gitea means backing up two folders: ./gitea-data and ./postgres-data, plus the docker-compose.yml file. Schedule that backup like any other service on your VPS — those folders hold every repo and its history.

To update, the process is the same as for any other Docker Compose service:

cd /opt/gitea
docker compose pull
docker compose up -d

Before a major update (for example, moving to a new major version), read Gitea's release notes first and make sure a fresh backup exists. It's a trivial habit, but it saves you from the worst-case scenario: a failed update with no copy to fall back on.

One last security note: don't leave raw Gitea HTTP on port 3000 exposed to the internet without protection for long-term use. Put a reverse proxy with HTTPS in front of it, or restrict access through a VPN — the same pattern as other self-hosted services on your VPS.

Final Checklist

  • docker compose ps shows gitea and gitea-db as running
  • The installation wizard is complete and you can log in as admin
  • Your SSH key is added to your account, and clone + push over port 222 work
  • One old repo has been migrated with --mirror, including all its branches and tags
  • Backups of ./gitea-data and ./postgres-data are scheduled
  • Web access is secured (HTTPS via reverse proxy, or restricted via VPN)

Closing

With Gitea, your code comes home to your own server without giving up convenience: you still get a pleasant web UI, still get pull requests and issues, and even still get CI. And because it's light, the small VPS you already have is very likely enough — no server upgrade just to host Git.

Also read: Deploy Otomatis ke VPS dengan GitHub Actions, Docker Compose untuk Self-Hosting dari Nol, and VPS Pertama: Setup Ubuntu dan Hardening SSH.

How many repos do you still have sitting on free services? Tell me in the comments — and which repo would you move to your own Gitea first?

Komentar