How to Self-Host Gitea and Access Your Git Server from Anywhere
Gitea is a lightweight, open-source Git service you can run on your own hardware. You get private repositories, pull requests, issue tracking, and a web interface similar to GitHub with no monthly fees, no storage limits, and no third party holding your code. This guide covers installing Gitea with Docker Compose, completing the setup wizard, and making both the web interface and SSH cloning work from anywhere using Localtonet.
📋 What's in this guide
What Is Gitea?
Gitea is an open-source Git hosting platform written in Go. It runs as a single binary with minimal resource requirements, making it practical on a home server, a Raspberry Pi, or a small VPS. The web interface covers everything you would expect from a modern Git platform: repository browsing, pull requests, code review, issue tracking, milestones, wikis, release management, and webhooks.
The main reason people self-host Gitea instead of using GitHub or GitLab is control. Your repositories live on your hardware. You set the storage limits. You decide who has access. There are no account suspensions, no policy changes, and no subscription required to keep private repositories private.
Self-Hosted Gitea
- Complete ownership of your code
- Unlimited private repositories
- No storage limits beyond your hardware
- No account bans or policy enforcement
- Works fully offline on a local network
- Free for any number of users
GitHub / GitLab.com
- Code stored on third-party servers
- Storage and bandwidth limits on free plans
- Subject to platform policy changes
- Private repos require a paid plan on some tiers
- No offline access
Install Gitea with Docker Compose
Docker Compose is the simplest and most portable way to run Gitea. The setup below uses SQLite as the database, which is perfectly fine for personal use and small teams. If you expect many concurrent users or heavy repository traffic, replace SQLite with PostgreSQL an example is included at the end of this section.
Create the project directory
mkdir gitea && cd gitea
Create the Docker Compose file
Create a docker-compose.yml file with the following content.
Note the SSH port mapping: Gitea's internal SSH runs on port 22 inside the container,
mapped to port 2222 on the host to avoid conflicting with the host's own SSH server.
networks:
gitea:
external: false
services:
server:
image: docker.gitea.com/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
restart: unless-stopped
networks:
- gitea
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:22"
Start Gitea
docker compose up -d
docker compose ps
Gitea is now running at http://localhost:3000. Open that address in your browser to continue with the setup wizard.
Optional: PostgreSQL for team or production use
Add a PostgreSQL service to your Compose file if you need better performance under concurrent load.
networks:
gitea:
external: false
services:
server:
image: docker.gitea.com/gitea:latest
container_name: gitea
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=changeme
restart: unless-stopped
networks:
- gitea
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:22"
depends_on:
- db
db:
image: postgres:16
restart: unless-stopped
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=changeme
- POSTGRES_DB=gitea
networks:
- gitea
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Replace changeme with a strong password before starting the stack. Use the same password in both the server and db service definitions.
Complete the Setup Wizard
The first time you open Gitea in your browser, it runs an installation wizard. Most settings can stay at their defaults, but a few need attention.
Database settings
If you are using the SQLite-only Compose file, select SQLite3 as the database type.
For the PostgreSQL setup, select PostgreSQL and enter db as the host,
port 5432, and the credentials from your Compose file.
SSH server domain and port
Set SSH Server Domain to localhost for now.
You will update this after the Localtonet tunnel is running.
Set SSH Server Port to 2222 to match the host port in the Compose file.
Gitea base URL
Set Gitea Base URL to http://localhost:3000 for now.
You will update this after the HTTP tunnel is set up and you have a public URL.
Create the administrator account
Scroll to the bottom of the wizard and fill in the Administrator Account Settings. Choose a username, a strong password, and an email address. Click Install Gitea. Gitea redirects you to the dashboard and your server is ready.
Access the Gitea Web Interface from Anywhere
Gitea's web interface runs on port 3000. To reach it from outside your local network,
create an HTTP tunnel in Localtonet. The tunnel gives you a public HTTPS URL and handles TLS automatically.
Gitea itself requires no changes.
Install and authenticate Localtonet on the host machine
curl -fsSL https://localtonet.com/install.sh | sh
localtonet --authtoken <YOUR_TOKEN>
Create an HTTP tunnel for port 3000
Log in to the Localtonet dashboard, go to Tunnels → New Tunnel,
select HTTP, set local IP to 127.0.0.1 and port to 3000.
Click Create. The dashboard generates a public HTTPS URL such as https://abc123.localto.net.
Update the Gitea base URL
Open Gitea, go to Site Administration → Configuration and update the Root URL to your Localtonet HTTPS URL. Alternatively, edit the config file directly on the host:
# The app.ini is stored inside the data volume
nano ./data/gitea/conf/app.ini
Find the [server] section and update these two values:
[server]
DOMAIN = abc123.localto.net
ROOT_URL = https://abc123.localto.net/
Restart Gitea to apply the change:
docker compose restart server
Your Gitea web interface is now reachable at the HTTPS URL from any browser, anywhere in the world.
SSH Git Cloning from Anywhere
The web interface handles browsing and management, but developers also need to push and pull over SSH.
Gitea's SSH server runs on port 2222 on the host. Create a second Localtonet TCP tunnel for this port.
Create a TCP tunnel for port 2222
In the Localtonet dashboard, go to Tunnels → New Tunnel,
select TCP, set local IP to 127.0.0.1 and port to 2222.
Click Create. The relay address will look like example.localto.net:33XX.
Update the SSH domain in Gitea config
Open ./data/gitea/conf/app.ini and update the SSH settings so Gitea shows the correct clone URL in the web interface.
[server]
DOMAIN = abc123.localto.net
ROOT_URL = https://abc123.localto.net/
SSH_DOMAIN = example.localto.net
SSH_PORT = 33221
docker compose restart server
Now when you open any repository in Gitea and click the SSH clone button, the URL shown already contains the relay address and port. Clone a repository from any machine:
git clone ssh://git@example.localto.net:33221/youruser/yourrepo.git
SSH cloning requires your public key to be registered in Gitea.
Go to Settings → SSH and GPG Keys → Add Key and paste the contents of your
~/.ssh/id_ed25519.pub (or id_rsa.pub) file.
Gitea uses this key to authenticate your git push and git pull commands.
Keep Everything Running After a Reboot
The Docker Compose file already has restart: unless-stopped on the Gitea container,
so it comes back automatically when Docker starts. To also bring the Localtonet tunnels back after a reboot,
register Localtonet as a systemd service on the host machine:
sudo localtonet --install-service --authtoken <YOUR_TOKEN>
sudo localtonet --start-service --authtoken <YOUR_TOKEN>
Confirm both services are running:
docker compose ps
systemctl status localtonet
After this, a full power cycle brings Gitea and both tunnels back without any manual steps. Your Git server and its public URLs are available again within seconds of the machine booting.
Security Recommendations
🔑 Disable registration after creating your accounts
A publicly reachable Gitea instance with open registration can be found and abused by bots. Once you and your team have created accounts, go to Site Administration → Configuration → User settings and disable Allow Self-Registration. New users can only be added by an administrator from that point on.
🔒 Use SSH keys, not passwords, for Git operations
Password-based Git authentication over HTTPS is less secure and more friction than SSH keys.
Add your SSH public key to Gitea and use git clone ssh://... URLs for all push and pull operations.
You can also disable password authentication for Git over HTTP in the Gitea admin settings.
🌍 Do not bind port 3000 or 2222 to a public interface
The Compose file above does not specify a host IP for the port bindings, which means they listen on all interfaces.
For a tighter setup, change the port bindings to 127.0.0.1:3000:3000 and 127.0.0.1:2222:22.
This ensures only Localtonet (running on the same machine) can reach those ports.
💾 Back up the data volume regularly
All of Gitea's repositories, configuration, and user data live in the ./data directory.
Back this directory up regularly to an external location. A simple approach is a daily cron job that tars
the directory and copies it to a remote storage service.
🔐 Add Localtonet SSO for an extra login layer
Enable Single Sign-On on your HTTP tunnel in the Localtonet dashboard. Anyone reaching your Gitea URL must authenticate via Google, GitHub, Microsoft, or GitLab before the Gitea login page even loads. This protects Gitea from brute-force login attempts on the public URL.
Frequently Asked Questions
Can I run Gitea on a Raspberry Pi?
Yes. Gitea is written in Go and compiles to a single binary for ARM and ARM64. The official Docker image supports both architectures. A Raspberry Pi 3 or 4 handles Gitea well for personal use and small teams. Use SQLite as the database to keep resource usage minimal.
Will I lose my repositories if I update the Docker image?
No, as long as you use a volume for the data directory as shown in the Compose file. Pull the new image and recreate the container the data volume persists. Always back up the ./data directory before upgrading, just in case.
Can I migrate my GitHub or GitLab repositories to Gitea?
Yes. Gitea has a built-in migration tool under Create → Migrate Repository. It supports GitHub, GitLab, Bitbucket, Gogs, and plain Git URLs. For GitHub migrations, you can provide a personal access token to also import issues, pull requests, labels, and milestones.
The Localtonet relay address changes. How do I keep a stable clone URL?
Reserve a fixed port for the TCP tunnel via Add-ons → Reserved Ports in the Localtonet dashboard. For the web interface, attach a custom domain to the HTTP tunnel. Both options give you a permanent address that never changes between restarts. Check out our custom domain guide for setup instructions.
Can I use Gitea Actions for CI/CD?
Yes. Gitea Actions is built into Gitea and is compatible with GitHub Actions syntax. You need to run a separate act_runner process to execute the workflow jobs. The runner can run on the same machine as Gitea or on a separate host. Enable Gitea Actions under Site Administration → Configuration → Repository settings.
Is Gitea a good replacement for GitHub for a small team?
For most small team workflows code review, pull requests, issue tracking, CI/CD via webhooks or Gitea Actions, yes. The features that Gitea does not replicate are GitHub-specific things like GitHub Pages, GitHub Marketplace integrations, and the social discovery features. If your team mainly needs a private code host with a familiar workflow, Gitea covers it well.
Your Own Private Git Server, Accessible from Anywhere
Run Gitea with Docker Compose, open two Localtonet tunnels, and your team gets a private Git server with a public web interface and SSH cloning from any location in the world.
Create Free Localtonet Account →