If you've been bouncing repositories between a USB stick, a private GitHub repo you don't fully trust, and "the folder on my desktop," it might be time to run your own Git server. Gitea is a small, fast, self-hosted alternative to GitHub or GitLab, and it runs comfortably in an LXC container on Proxmox VE with barely any resources. On a fresh 9.2 install, the whole setup below takes about 15 minutes, most of which is waiting for apt to finish.
This guide walks through creating a Debian 13 container, installing Gitea from the official binary, and getting your first repository pushed. No Docker, no reverse proxy required to start — just a container, a systemd service, and a web browser.
What You Will Learn
By the end of this tutorial you'll know how to:
- Create a lightweight, unprivileged LXC container for Gitea on Proxmox VE 9.2
- Install Gitea 1.27.0 from the official binary release, not a random third-party script
- Set up the
gitsystem user, data directories, and a systemd service so Gitea survives reboots - Finish the web-based setup wizard and create your first repository
- Fix the handful of things that trip up almost everyone the first time: SSH port conflicts, wrong ROOT_URL, and permission errors
What Is This Feature?
Gitea is open-source software that gives you your own private GitHub. It handles Git repositories, pull requests, issues, a wiki, and basic CI hooks, all through a web interface that looks and feels a lot like GitHub's. It's written in Go, ships as a single binary, and the whole thing — binary, dependencies, and a small SQLite database — fits in well under 200 MB of disk.
An LXC container is Proxmox's lightweight virtualization option. Instead of emulating a full virtual machine with its own kernel, an LXC container shares the host's kernel and only isolates the processes and filesystem. That means it boots in a couple of seconds and uses a fraction of the RAM a VM would need for the same job — which matters here, because Gitea itself barely needs any resources at all.
You'll be running Gitea directly on the container's Debian install rather than inside Docker. That's a deliberate choice for this tutorial: fewer moving parts to reason about when something goes wrong, and it's a good way to actually understand what Gitea needs to run.
Why Would You Use It?
A few reasons people end up here:
Maybe you're tired of paying for private repos, or you don't love the idea of your side projects sitting on someone else's servers. Maybe your homelab already has a Proxmox box doing nothing most of the day, and a git server is a natural thing to add to it. Or maybe you just want a private place to store configuration files, Ansible playbooks, and Proxmox Helper Script forks without making them public. Gitea is also just light. GitLab's official Docker image alone wants 4 GB of RAM as a baseline recommendation. Gitea runs fine in a container with 1 GB. For a homelab, or for a small team that doesn't need GitLab's CI runners and container registry, that difference matters.
Prerequisites
Before starting, make sure you have:
- Proxmox VE 9.2 (or any recent 8.x/9.x release — the steps below don't depend on anything new in 9.2 specifically)
- A Debian 13 ("Trixie") container template downloaded, or you can grab it during setup
- At least 8 GB of free storage and 1 GB of RAM you can spare for the container
- A static IP address planned for the container, or at least a DHCP reservation — Gitea's URLs get baked into its config, and changing the address later means editing a config file
- Comfortable using the Proxmox shell or the container console — this tutorial is command-line heavy
You don't need a domain name or SSL certificate to follow along. This tutorial gets Gitea running over plain HTTP on your local network. Putting it behind a reverse proxy with HTTPS is worth doing before you expose it to the internet, but that's a separate project.
Step-by-Step Tutorial
1. Download the Debian 13 template
On the Proxmox host, update the list of available templates and download the current Debian 13 standard image:
pveam update
pveam available | grep debian-13
pveam download local debian-13-standard_13.x-x_amd64.tar.zst
Replace the filename in the last command with whatever version pveam available actually lists — Debian releases point updates fairly often, and the exact patch number changes. You can also do this from the GUI: local (Storage) > CT Templates > Templates, search for "debian-13", and click Download.
2. Create the container
In the Proxmox web interface, click Create CT in the top-right corner. Work through the wizard with these settings:
- General: pick a hostname like
gitea, set a root password, leave "Unprivileged container" checked - Template: the debian-13-standard template you just downloaded
- Disk: 8 GB is plenty for a small team; bump it up if you expect large repos or a lot of Git LFS objects
- CPU: 1 core is fine to start
- Memory: 1024 MB, with little or no swap — Gitea's memory footprint under normal homelab load is usually well under 200 MB
- Network: assign a static IPv4 address here if you know it, e.g.
192.168.1.50/24with your router as gateway
Leaving the container unprivileged is the right call here. There's nothing in this setup that needs raw device access or kernel capabilities an unprivileged container can't provide.
Start the container after creation, then open its console (the >_ Console button) and log in as root.
3. Install dependencies
Update the package list and install the small set of packages Gitea needs:
apt update && apt upgrade -y
apt install -y git sqlite3 wget
git is the version control system itself — Gitea shells out to it for the actual repository operations. sqlite3 gives you a simple file-based database, which is more than enough for a personal or small-team instance. wget just downloads the Gitea binary in the next step.
4. Create the git system user
adduser --system --shell /bin/bash --gecos 'Git Version Control' \
--group --disabled-password --home /home/git git
Gitea should never run as root. This creates a dedicated, low-privilege system account named git that owns the Gitea process and all repository data. If a vulnerability in Gitea (or in some repository hook) were ever exploited, the damage stays contained to what this one account can touch.
5. Set up the data directories
mkdir -p /var/lib/gitea/{custom,data,log}
chown -R git:git /var/lib/gitea
chmod -R 750 /var/lib/gitea
mkdir /etc/gitea
chown root:git /etc/gitea
chmod 770 /etc/gitea
/var/lib/gitea holds everything Gitea generates: repositories, uploaded avatars, logs, and session data. /etc/gitea holds the single app.ini configuration file. Giving the git group write access to /etc/gitea lets the setup wizard write that file later without you needing to run anything as root.
6. Download and install the Gitea binary
cd /tmp
wget -O gitea https://dl.gitea.com/gitea/1.27.0/gitea-1.27.0-linux-amd64
chmod +x gitea
mv gitea /usr/local/bin/gitea
This pulls the official 64-bit Linux binary straight from Gitea's own download server. There's no installer script here and nothing to compile — it's a single ~140 MB executable with everything bundled in, including the web assets.
7. Create the systemd service
Create /etc/systemd/system/gitea.service with the following content:
[Unit]
Description=Gitea (Git with a cup of tea)
After=network.target
[Service]
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
[Install]
WantedBy=multi-user.target
Then enable and start it:
systemctl daemon-reload
systemctl enable --now gitea
systemctl status gitea
You should see active (running) in the status output. If you don't, jump ahead to the Troubleshooting section — the fix is almost always a permissions issue on one of the directories from step 5.
8. Finish setup in the browser
From a machine on the same network, open http://<container-ip>:3000. You'll land on Gitea's first-run setup page.
Most of the defaults are fine. The two fields worth double-checking:
- Database Type: leave it on SQLite3 unless you already run a Postgres or MySQL server you want to point it at
- Gitea Base URL: make sure this matches the address you'll actually use to reach the container, e.g.
http://192.168.1.50:3000/. Getting this wrong is the single most common source of confusion later, covered in Troubleshooting below
Scroll down to the Administrator Account Settings section and create your first user — this becomes the site admin. Click Install Gitea, wait a few seconds, and you'll be redirected to the sign-in page.
9. Push your first repository
Log in, click the + icon in the top bar, and choose New Repository. Give it a name and create it. Gitea shows you the clone URL immediately — for a brand new empty repo it'll look something like:
git clone http://192.168.1.50:3000/yourname/yourrepo.git
From your workstation:
git clone http://192.168.1.50:3000/yourname/yourrepo.git
cd yourrepo
echo "# My First Repo" > README.md
git add README.md
git commit -m "Initial commit"
git push origin main
Refresh the repository page in Gitea and you'll see the commit and the README rendered. That's the whole loop working end to end.
Commands Explained
| Command | What it does |
|---|---|
pveam download local <template> | Downloads a container template into the specified storage so it's available when creating a new CT |
adduser --system ... git | Creates a non-login-by-default system account dedicated to running Gitea, rather than using root |
chown -R git:git /var/lib/gitea | Hands ownership of the data directory tree to the git user and group, so the Gitea process can read and write its own files |
chmod 750 /var/lib/gitea | Restricts the data directory to the owner and group only — other local users on the container can't browse repository contents |
systemctl enable --now gitea | Starts the Gitea service immediately and also enables it to start automatically the next time the container boots |
systemctl status gitea | Shows whether the service is running, and prints the last few log lines — the first place to look when something's wrong |
Common Errors
"500: Internal Server Error" right after finishing the install wizard. This is almost always a permissions problem — the git user couldn't write to /etc/gitea or /var/lib/gitea during setup. Re-check the ownership from step 5: chown -R git:git /var/lib/gitea and chown root:git /etc/gitea with mode 770.
listen tcp 0.0.0.0:3000: bind: address already in use in the systemd logs. Something else is already using port 3000 — check with ss -tlnp | grep 3000. If it's a leftover Gitea process from a previous failed attempt, systemctl stop gitea and check for orphaned processes with ps aux | grep gitea.
Repository links point to the wrong address, or "cross-origin" style errors show up in the browser console. The ROOT_URL in /etc/gitea/app.ini doesn't match how you're actually accessing Gitea. Fix it directly in the config:
nano /etc/gitea/app.ini
# under [server], set:
ROOT_URL = http://192.168.1.50:3000/
systemctl restart gitea
Git push over SSH asks for a password or fails with "Permission denied (publickey)." By default, Gitea installed from binary doesn't run its own SSH server — it relies on the container's existing OpenSSH daemon and the git user's authorized_keys file. Make sure your public key is actually added through Gitea's web UI under Settings > SSH / GPG Keys, not just sitting on your workstation. HTTP clone URLs sidestep this entirely if you'd rather not deal with SSH keys yet.
Troubleshooting
Start with the logs. journalctl -u gitea -n 100 --no-pager shows the last hundred lines from the service, which covers most startup failures. Gitea also keeps its own application log at /var/lib/gitea/log/gitea.log, which is more detailed and includes things like failed login attempts and webhook delivery errors.
If the container can't reach the outside world during apt update, check the container's network configuration in the Proxmox GUI first (Resources > Network Device), and confirm the bridge (usually vmbr0) actually has a path to your router. This trips up more people than any Gitea-specific setting — it's worth ruling out before you start debugging Gitea itself.
If you enabled the Proxmox firewall at the datacenter or node level, remember it applies to containers too. You'll need a rule allowing TCP port 3000 (and 22, if you plan to use SSH cloning) inbound to the container's IP, or nothing outside the container will be able to reach it even though systemctl status shows it running happily.
One more one that catches people out: if you want Gitea to run its own built-in SSH server instead of relying on the OS's sshd (useful if you're running Gitea in Docker, less necessary here), you'd set START_SSH_SERVER = true in app.ini. Don't leave SSH_PORT at the default of 22 in that case — the container's own OpenSSH daemon is already bound to that port, and the two will conflict. Pick something like 2222 instead.
Best Practices
Take a snapshot of the container right after you finish the install wizard. It's a two-second operation in Proxmox and gives you a clean rollback point before you start adding real repositories.
Back up /etc/gitea/app.ini and /var/lib/gitea together — the config file and the data directory need to travel as a pair. A vzdump backup of the whole container covers this without any extra thought, which is honestly the simplest approach for a single-purpose container like this one.
Don't expose port 3000 directly to the internet. If you want Gitea reachable outside your home network, put it behind a reverse proxy (Nginx Proxy Manager works well here) with a real TLS certificate, and only forward 443 through your router — never the raw Gitea port.
Turn on two-factor authentication for your admin account under Settings > Security once you're past initial setup. It's a Git server holding your source code and possibly secrets in config files — worth the thirty seconds it takes to enable.
Frequently Asked Questions
Do I need Docker to run Gitea on Proxmox?
No. This tutorial installs Gitea directly as a systemd service inside an LXC container. Docker is a valid alternative if you already run other containerized apps and prefer that workflow, but it's not required.
How much storage will Gitea actually need?
For a handful of personal repositories, well under 1 GB. Storage use scales with your repository history and any binary assets you commit — video files and large datasets will eat through 8 GB fast if you're not using Git LFS.
Can I migrate my GitHub repositories into Gitea?
Yes. Gitea has a built-in migration tool under New Migration that can pull directly from GitHub, GitLab, and a few other hosts, including issues and pull request history if you provide a personal access token.
Is SQLite good enough, or should I use PostgreSQL?
SQLite handles a single-user or small-team instance without any trouble. Move to PostgreSQL if you expect dozens of concurrent users or heavy CI activity hitting the API — most homelab setups never get there.
Will this survive a Proxmox host reboot?
Yes, as long as you set the container to start on boot (Options > Start at boot in the container's settings) and left systemctl enable in place for the Gitea service itself.
Conclusion
You now have a working Git server running in a container that uses less RAM than a browser tab. From here, the natural next steps are putting it behind a reverse proxy with HTTPS, setting up scheduled backups through Proxmox's own vzdump jobs, and maybe connecting a Gitea Actions runner if you want CI without leaving your own network. None of that is required to get value out of what you just built — a private place to push code is useful on its own, starting today.