Running Your Own Git Server Without the Corporate Strings
GitHub and GitLab are convenient until they are not. Rate limits, storage quotas, policy changes, and the general anxiety of storing proprietary code on someone else’s infrastructure have pushed a growing number of developers toward self-hosted alternatives. Forgejo sits at the top of that list – a community-driven fork of Gitea that offers a full Git hosting experience, including repositories, pull requests, issue tracking, CI/CD pipelines via Forgejo Actions, and user management, all running on your own hardware or VPS.
What makes Forgejo worth setting up over a raw Git server or a heavier platform like GitLab CE is the balance it strikes. GitLab requires substantial RAM and CPU headroom. A bare Git server gives you no web interface at all. Forgejo runs comfortably on a $5-per-month VPS with 1GB of RAM and delivers a web UI that feels familiar to anyone who has used GitHub. This guide walks through a complete Docker-based installation on a Linux server, including database configuration, reverse proxy setup, and basic hardening.
Prerequisites and Server Preparation
You need a Linux server running Ubuntu 22.04 or Debian 12, Docker and Docker Compose installed, a domain name pointed at your server’s IP address, and ports 80 and 443 open in your firewall. The setup below uses Docker Compose for the Forgejo container alongside a PostgreSQL database container, and Nginx as a reverse proxy with a Let’s Encrypt SSL certificate. If you are already running Docker workloads and managing them through a visual interface, Portainer can sit alongside Forgejo without any conflict.
Start by creating a dedicated directory for the project and a system user Forgejo will run as. On your server, run sudo adduser –system –shell /bin/bash –gecos “Git” –group –disabled-password –home /home/git git to create a git user. Then create your project directory: sudo mkdir -p /opt/forgejo && sudo chown git:git /opt/forgejo. This separation ensures the Forgejo process does not run as root and that its data volume is owned by a predictable user, which matters when you later mount volumes in the Compose file.
Install Nginx and Certbot before writing any configuration files. Run sudo apt update && sudo apt install -y nginx certbot python3-certbot-nginx. Once installed, stop Nginx temporarily with sudo systemctl stop nginx so Certbot can bind to port 80 during certificate issuance. Request your certificate with sudo certbot certonly –standalone -d git.yourdomain.com, replacing the domain with your actual subdomain. Certbot stores the certificate and private key in /etc/letsencrypt/live/git.yourdomain.com/, which you will reference in the Nginx config shortly.
Writing the Docker Compose File
Navigate to /opt/forgejo and create a file named docker-compose.yml. The configuration below defines two services: a PostgreSQL database and the Forgejo application itself.
version: "3.8"
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: strongpasswordhere
POSTGRES_DB: forgejo
volumes:
- ./postgres-data:/var/lib/postgresql/data
networks:
- forgejo-net
forgejo:
image: codeberg.org/forgejo/forgejo:7
restart: always
depends_on:
- db
environment:
- USER_UID=1000
- USER_GID=1000
- FORGEJO__database__DB_TYPE=postgres
- FORGEJO__database__HOST=db:5432
- FORGEJO__database__NAME=forgejo
- FORGEJO__database__USER=forgejo
- FORGEJO__database__PASSWD=strongpasswordhere
volumes:
- ./forgejo-data:/data
ports:
- "3000:3000"
- "2222:22"
networks:
- forgejo-net
networks:
forgejo-net:
Replace strongpasswordhere with a real password in both places – they must match. The USER_UID and USER_GID values should correspond to the git user you created earlier. Check with id git on the command line and update accordingly. Port 3000 is the Forgejo web interface, while port 2222 exposes SSH access for Git operations so it does not collide with your server’s own SSH daemon on port 22. Start the stack with docker compose up -d and watch the logs with docker compose logs -f forgejo until you see the application report that it is listening.
Configuring Nginx and Completing the Installation
With Forgejo running on port 3000 internally, Nginx handles incoming HTTPS traffic and proxies it through. Create a new Nginx server block at /etc/nginx/sites-available/forgejo with the following content:
server {
listen 80;
server_name git.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name git.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem;
client_max_body_size 100M;
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;
}
}
Enable the site with sudo ln -s /etc/nginx/sites-available/forgejo /etc/nginx/sites-enabled/, test the configuration with sudo nginx -t, and start Nginx with sudo systemctl start nginx. The client_max_body_size 100M directive is worth keeping – Forgejo’s default repository file upload limit is set at the application level, but Nginx will reject large pushes before they even reach the app if this value is too low. Adjust it upward if you plan to store large binary assets or use Git LFS.
Open your browser and navigate to https://git.yourdomain.com. Forgejo’s web installer will appear. The database fields should pre-populate from your environment variables, but verify them. Set the site title, base URL (use your full HTTPS domain), and create the administrator account on this screen. Do not skip the administrator setup – if you close this page before completing it, you will need to create the admin account manually through the Forgejo CLI inside the container. Once submitted, Forgejo redirects you to the login page.
After logging in as admin, go to Site Administration > Settings and disable public registration if this instance is for private or team use only. You can still invite specific users by email from the admin panel. Also review the app.ini file that Forgejo generates inside the forgejo-data/gitea/conf/ directory. This file controls nearly every aspect of the application’s behavior – from SSH key types to mailer configuration. Changes made here require a container restart to take effect via docker compose restart forgejo.
SSH-based Git access works through port 2222. Users need to add their SSH public key under User Settings > SSH/GPG Keys and then clone repositories using the syntax git clone ssh://git@git.yourdomain.com:2222/username/repo.git. If you want to avoid specifying the port on every operation, users can add a Host block to their local ~/.ssh/config file that maps git.yourdomain.com to port 2222 automatically. Forgejo Actions, the built-in CI/CD system, requires registering a runner separately – the runner is a standalone binary or container that polls Forgejo for pending workflow jobs, and it needs to be installed and registered with an API token generated from the admin panel before any .forgejo/workflows/ YAML files will execute.
Frequently Asked Questions
What are the minimum server requirements for Forgejo?
Forgejo runs on as little as 1GB of RAM. A basic VPS with 1 vCPU and 1GB RAM is sufficient for small teams or personal use.
Can I migrate repositories from GitHub to Forgejo?
Yes. Forgejo includes a built-in migration tool under the new repository screen that can import repos, issues, and pull requests from GitHub, GitLab, and Gitea via API token.
