
This article provides a guide demonstrating how to install Forgejo on Ubuntu VPS.
What is Forgejo?
Forgejo is a lightweight, self-hosted Git software forge that provides Git repository hosting, pull requests, issues, organizations, releases, packages, wikis, CI/CD integration through Forgejo Actions, and many of the collaboration features associated with services such as GitHub and GitLab.
This guide walks through a production-oriented installation of Forgejo on an Ubuntu VPS using:
- Ubuntu 24.04 LTS or a comparable supported Ubuntu release
- Forgejo’s official Linux binary
- A dedicated
gitsystem account - PostgreSQL
- systemd
- Nginx as a reverse proxy
- Let’s Encrypt SSL/TLS
- SSH-based Git access
- Git LFS
- UFW firewall protection
Forgejo officially supports installation from a standalone binary, and its documentation describes this general layout using /var/lib/forgejo for application data and /etc/forgejo for configuration.
For this guide, we’ll use:
git.example.com
Replace that hostname everywhere with the actual hostname you intend to use.
VPS Requirements
Forgejo itself is relatively lightweight. A practical starting VPS configuration is:
CPU: 1-2 vCPU RAM: 2 GB+ Storage: 20 GB+ SSD/NVMe OS: Ubuntu 24.04 LTS
Storage requirements depend much more heavily on the repositories, packages, releases, Actions artifacts, and Git LFS objects you intend to host than on Forgejo itself.
For a small private development environment, something such as:
2 vCPU 4 GB RAM 50-100 GB NVMe
provides considerable room for growth.
Compare Ubuntu VPS Plans
How to Install Forgejo on Ubuntu VPS
To install Forgejo on Ubuntu VPS, follow the steps outlined below:
-
Prepare the VPS
-
Connect to the Server
ssh root@SERVER_IP
Or, preferably, connect using an existing sudo-enabled administrative account:
ssh username@SERVER_IP
Then obtain a root shell if desired:
sudo -i
-
Update Ubuntu
Update the package lists:
apt update
Install available upgrades:
apt upgrade -y
If the upgrade installs a new kernel, reboot before continuing:
reboot
Reconnect after the VPS comes back online.
-
Configure the Hostname
A useful hostname might be:
git.example.com
Set it with:
hostnamectl set-hostname git.example.com
Verify:
hostnamectl
You should see:
Static hostname: git.example.com
-
-
Configure DNS
-
Create the DNS Record
At your DNS provider, create an
Arecord:Type: A Name: git Value: SERVER_IP TTL: 300
For example:
git.example.com -> 203.0.113.25
If the VPS has IPv6, you can also create an
AAAArecord.Verify DNS resolution:
See Also: Easily Deploy NodeBB Community Forum on Ubuntu VPS
dig +short git.example.com
It should return the VPS’s public IP address.
Do not proceed with Let’s Encrypt until the hostname resolves to this server.
-
-
Install Dependencies
-
Install Required Packages
Install Git, Git LFS, Nginx, PostgreSQL, Certbot, GnuPG, curl, wget, and supporting utilities:
apt install -y \ git \ git-lfs \ nginx \ postgresql \ postgresql-contrib \ certbot \ python3-certbot-nginx \ gnupg \ curl \ wget \ ca-certificatesInitialize Git LFS:
git lfs install --system
Check Git:
git --version
Check Nginx:
nginx -v
Forgejo specifically recommends having both Git and Git LFS installed when using its binary installation method.
-
-
Create the Forgejo User
-
Create the
gitAccountForgejo should not run as root.
Create a dedicated system account:
adduser --system \ --shell /bin/bash \ --gecos 'Git Version Control' \ --group \ --disabled-password \ --home /home/git \ gitThis follows Forgejo’s recommended Ubuntu/Debian installation model.
Verify:
id git
You should see the
gituser and group.The account is important because normal SSH repository URLs will eventually look like:
git@git.example.com:username/repository.git
-
-
Install Forgejo
-
Determine the Server Architecture
Run:
dpkg --print-architecture
Most VPS servers will return:
amd64
ARM VPS servers will typically return:
arm64
You can also check with:
uname -m
Typical results are:
x86_64
or:
aarch64
-
Download Forgejo
Forgejo publishes signed Linux binaries. At the time this guide was prepared, Forgejo’s download page lists 16.0.3 for its current Linux amd64 example. Always check the official download page before installing rather than assuming that version remains current.
For example:
cd /tmp
Set the version:
FORGEJO_VERSION="16.0.3"
Download it:
wget https://code.forgejo.org/forgejo/forgejo/releases/download/v${FORGEJO_VERSION}/forgejo-${FORGEJO_VERSION}-linux-amd64Download its signature:
wget https://code.forgejo.org/forgejo/forgejo/releases/download/v${FORGEJO_VERSION}/forgejo-${FORGEJO_VERSION}-linux-amd64.ascFor an ARM64 server, substitute the appropriate ARM64 release artifact.
-
Verify the Forgejo Download
For a production server, don’t skip binary verification.
Import Forgejo’s signing key according to the key information published on the official Forgejo download page, and verify the downloaded signature.
The basic verification operation is:
gpg --verify \ forgejo-${FORGEJO_VERSION}-linux-amd64.asc \ forgejo-${FORGEJO_VERSION}-linux-amd64Do not install the binary if GPG reports an invalid signature.
Forgejo signs its official binaries specifically so administrators can verify that the downloaded executable is authentic.
-
Install the Binary
Copy it into
/usr/local/bin:cp forgejo-${FORGEJO_VERSION}-linux-amd64 /usr/local/bin/forgejoSet appropriate permissions:
chmod 755 /usr/local/bin/forgejo
Verify:
See Also: How to Install Pixelfed on Ubuntu VPS (8 Step Quick-Start Guide)
forgejo --version
You should now see the installed Forgejo version.
-
-
Create Forgejo’s Directories
-
Create the Data Directory
Create:
mkdir -p /var/lib/forgejo
Set ownership:
chown git:git /var/lib/forgejo
Set permissions:
chmod 750 /var/lib/forgejo
Forgejo will store persistent application data here, including repository-related data.
-
Create the Configuration Directory
Run:
mkdir -p /etc/forgejo
Set ownership:
chown root:git /etc/forgejo
Initially allow Forgejo to write its configuration:
chmod 770 /etc/forgejo
Forgejo’s primary configuration will ultimately reside at:
/etc/forgejo/app.ini
Forgejo recommends initially making this directory writable by the application so the web installer can create
app.ini, then tightening its permissions after installation.
-
-
Configure PostgreSQL
-
Why PostgreSQL?
Forgejo supports several database options, including SQLite, MySQL/MariaDB, and PostgreSQL.
SQLite is perfectly reasonable for very small installations. Forgejo’s documentation notes that SQLite may be sufficient even for installations with multiple users.
For a production VPS, however, PostgreSQL provides a better foundation for future growth.
Start and enable PostgreSQL:
systemctl enable --now postgresql
Check it:
systemctl status postgresql
-
Create a PostgreSQL Database
Generate a strong password first:
openssl rand -base64 32
Save it securely.
Enter PostgreSQL:
sudo -u postgres psql
Create the Forgejo database user:
CREATE USER forgejo WITH PASSWORD 'REPLACE_WITH_STRONG_PASSWORD';
Create the database:
CREATE DATABASE forgejo OWNER forgejo;
Exit:
\q
You now have:
Database: forgejo User: forgejo Password: your-generated-password
-
Test PostgreSQL
Run:
psql -h 127.0.0.1 -U forgejo -d forgejo
Enter the password.
If successful, you’ll reach:
forgejo=>
Exit:
\q
-
-
Install the Forgejo systemd Service
-
Download the Official Service Unit
Forgejo provides a systemd unit file.
Install it with:
wget -O /etc/systemd/system/forgejo.service \ https://codeberg.org/forgejo/forgejo/raw/branch/forgejo/contrib/systemd/forgejo.service
Forgejo’s binary installation documentation uses this service unit for systemd deployments.
Because we’re using PostgreSQL, inspect the file:
nano /etc/systemd/system/forgejo.service
Look for the PostgreSQL-related
Wants=andAfter=directives mentioned in the service file and enable the appropriate PostgreSQL dependencies.Save the file.
Reload systemd:
systemctl daemon-reload
Enable Forgejo:
systemctl enable forgejo
Start it:
systemctl start forgejo
-
Verify Forgejo
Check:
systemctl status forgejo
You want:
Active: active (running)
Verify port 3000:
ss -lntp | grep 3000
You should see Forgejo listening on port 3000.
Test locally:
curl -I http://127.0.0.1:3000
If Forgejo won’t start, inspect its logs:
journalctl -u forgejo -n 100 --no-pager
Or follow them live:
See Also: How to Host a Discord Bot on Ubuntu VPS
journalctl -u forgejo -f
These are also the troubleshooting commands recommended by Forgejo’s installation documentation.
-
-
Configure Nginx
-
Why Use a Reverse Proxy?
Forgejo can provide HTTPS itself, but putting Nginx in front of Forgejo makes it easy to:
- serve Forgejo on ports 80/443;
- manage Let’s Encrypt certificates;
- redirect HTTP to HTTPS;
- apply additional security controls;
- accommodate other services on the VPS later.
Forgejo explicitly supports this deployment model.
-
Create the Nginx Virtual Host
Remove the default site if you don’t need it:
rm -f /etc/nginx/sites-enabled/default
Create:
nano /etc/nginx/sites-available/forgejo
Add:
server { listen 80; listen [::]:80; server_name git.example.com; merge_slashes off; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Connection $http_connection; proxy_set_header Upgrade $http_upgrade; 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; client_max_body_size 512M; } }The
merge_slashes offdirective is significant because Forgejo’s official Nginx configuration uses it to correctly handle URL-encoded slashes.Enable the site:
ln -s /etc/nginx/sites-available/forgejo /etc/nginx/sites-enabled/forgejo
Test Nginx:
nginx -t
You should receive:
syntax is ok test is successful
Reload:
systemctl reload nginx
-
-
Configure the Firewall
-
Enable UFW
First make absolutely certain SSH is permitted:
ufw allow OpenSSH
Allow HTTP:
ufw allow 80/tcp
Allow HTTPS:
ufw allow 443/tcp
Enable UFW:
ufw enable
Check:
ufw status
You should have access to:
22/tcp 80/tcp 443/tcp
Do Not Expose Port 3000
There is normally no reason for:
TCP/3000
to be publicly accessible.
Nginx communicates with Forgejo internally over:
127.0.0.1:3000
and visitors connect through ports 80/443.
-
-
Run the Forgejo Web Installer
-
Open Forgejo
Before SSL is installed, visit:
http://git.example.com
Nginx should proxy the request to Forgejo and display:
Initial Configuration
-
Configure PostgreSQL
Select:
Database Type: PostgreSQL
Use approximately:
Host: 127.0.0.1:5432 Username: forgejo Password: YOUR_DATABASE_PASSWORD Database Name: forgejo SSL: Disable
SSL isn’t normally necessary for this database connection because PostgreSQL and Forgejo reside on the same VPS.
-
Configure General Settings
Use:
Site Title: Forgejo
or your organization name.
Repository root:
/var/lib/forgejo/data/forgejo-repositories
Git LFS root:
/var/lib/forgejo/data/lfs
Run as username:
git
Server domain:
git.example.com
SSH server domain:
See Also: DNS Migration Guide (All-Inclusive 0-Downtime Strategy)
git.example.com
SSH port:
22
HTTP listen port:
3000
Forgejo Base URL will eventually be:
https://git.example.com/
Because HTTPS isn’t installed yet, you can initially complete installation over HTTP and correct
ROOT_URLimmediately afterward.
-
-
Create the Administrator
-
Administrator Account
Expand:
Administrator Account Settings
Create your initial administrator account.
Use:
Username: admin-or-your-username Email: administrator@example.com Password: strong-password
Using a unique administrator username rather than literally
adminis preferable.Click:
Install Forgejo
Forgejo will initialize the database and create:
/etc/forgejo/app.ini
-
-
Secure the Forgejo Configuration
-
Stop Forgejo
Once installation succeeds:
systemctl stop forgejo
-
Lock Down
app.iniRun:
chmod 750 /etc/forgejo chmod 640 /etc/forgejo/app.ini
Verify:
ls -l /etc/forgejo/app.ini
The configuration contains sensitive information—including your database credentials—so it should not be world-readable.
Forgejo recommends changing the configuration directory and
app.inito read-only access for the Forgejo account after the initial web configuration is complete.
-
-
Configure Forgejo for HTTPS
-
Edit
app.iniOpen:
nano /etc/forgejo/app.ini
Find:
[server]
Ensure the important values resemble:
[server] DOMAIN = git.example.com HTTP_PORT = 3000 ROOT_URL = https://git.example.com/ SSH_DOMAIN = git.example.com SSH_PORT = 22
Because Nginx terminates TLS, Forgejo itself can continue serving plain HTTP internally.
That is:
Internet | HTTPS :443 | Nginx | HTTP 127.0.0.1:3000 | Forgejo
This is a standard Forgejo-supported reverse-proxy arrangement.
Start Forgejo:
systemctl start forgejo
-
-
Install SSL/TLS
-
Obtain a Let’s Encrypt Certificate
Verify Nginx first:
nginx -t
Then:
certbot --nginx -d git.example.com
Enter your email address and accept the Let’s Encrypt terms.
When offered HTTP-to-HTTPS redirection, enable it.
Forgejo’s documentation specifically describes using Certbot with its Nginx reverse-proxy configuration and recommends changing
ROOT_URLto HTTPS afterward. -
Test HTTPS
Visit:
https://git.example.com
You should now have a valid certificate and be redirected from HTTP to HTTPS.
Check the redirect:
curl -I http://git.example.com
And HTTPS:
curl -I https://git.example.com
-
Test Certificate Renewal
Ubuntu’s Certbot installation normally creates automated renewal infrastructure.
Check:
systemctl status certbot.timer
Test renewal:
certbot renew --dry-run
Do not skip this test. A certificate that works today but doesn’t automatically renew will eventually cause an outage.
-
-
Verify Git over SSH
-
Create a Repository
Log into Forgejo and create a repository such as:
test-repo
On your workstation, add your SSH public key under:
User Settings → SSH / GPG Keys → Add Key
You can then clone using:
See Also: How to Host Your Own Mastodon Server on a VPS (5 Minute Quick-Start Guide)
git clone git@git.example.com:username/test-repo.git
Forgejo’s use of the dedicated
gitUnix account is what produces this familiar SSH URL format.
-
-
Test a Push
Enter the repository:
cd test-repo
Create a file:
echo "# Test Repository" > README.md
Add it:
git add README.md
Commit:
git commit -m "Initial commit"
Push:
git push origin main
The commit should immediately appear in Forgejo.
-
Disable Public Registration
For a private company or personal Forgejo server, you probably don’t want arbitrary Internet users registering accounts.
Open:
nano /etc/forgejo/app.ini
Add or modify:
[service] DISABLE_REGISTRATION = true
Restart:
systemctl restart forgejo
Administrators can then create users manually.
-
Configure Email
Forgejo can send email for:
- password resets;
- account notifications;
- issue notifications;
- pull request notifications;
- repository activity.
You can configure an SMTP provider in:
/etc/forgejo/app.ini
For example:
[mailer] ENABLED = true PROTOCOL = smtp+starttls SMTP_ADDR = mail.example.com SMTP_PORT = 587 USER = forgejo@example.com PASSWD = YOUR_SMTP_PASSWORD FROM = Forgejo <forgejo@example.com>
Restart afterward:
systemctl restart forgejo
Forgejo also supports sending through the system’s
sendmailimplementation if you prefer that architecture. -
Increase Upload Limits
Forgejo supports release attachments, repository uploads, packages, Git LFS, and other potentially large objects.
Nginx already contains:
client_max_body_size 512M;
You can increase that if required.
Forgejo also has its own repository upload settings. For example:
[repository.upload] FILE_MAX_SIZE = 512 MAX_FILES = 20
Forgejo documents these limits independently from the reverse proxy’s request-size limit.
Restart after changing
app.ini:systemctl restart forgejo
-
Useful Administration Commands
-
Service Management
Start Forgejo:
systemctl start forgejo
Stop:
systemctl stop forgejo
Restart:
systemctl restart forgejo
Status:
systemctl status forgejo
-
View Logs
Recent logs:
journalctl -u forgejo -n 100 --no-pager
Live logs:
journalctl -u forgejo -f
Logs since boot:
journalctl -u forgejo -b
-
Forgejo CLI
When running Forgejo administrative commands, Forgejo recommends running them as the
gituser and explicitly supplying the work and configuration paths.For example:
sudo -u git forgejo \ -w /var/lib/forgejo \ -c /etc/forgejo/app.ini \ admin user listA convenient wrapper can be created:
nano /usr/local/bin/forgejo-cli
Add:
#!/bin/sh exec sudo -u git \ /usr/local/bin/forgejo \ -w /var/lib/forgejo \ -c /etc/forgejo/app.ini \ "$@"Make it executable:
chmod 755 /usr/local/bin/forgejo-cli
Then:
forgejo-cli admin user list
-
-
Back Up Forgejo
A proper Forgejo backup strategy should protect at least:
See Also: 🛒 How to Add WooCommerce Store to Existing Website (10-Minute Quick Start Guide)
/etc/forgejo/ /var/lib/forgejo/ PostgreSQL database
-
Back Up PostgreSQL
For example:
mkdir -p /backup/forgejo
Then:
sudo -u postgres pg_dump forgejo | gzip > /backup/forgejo/forgejo-db-$(date +%F).sql.gz
-
Back Up Forgejo Data
For example:
tar -czf /backup/forgejo/forgejo-data-$(date +%F).tar.gz \ /var/lib/forgejo \ /etc/forgejoFor a serious production deployment, copy backups off the VPS. A backup stored only on the same server does not protect against VPS loss, storage corruption, accidental deletion of the instance, or provider-level failure.
-
-
Updating Forgejo
Forgejo publishes stable releases every three months, an annual LTS release, and more frequent patch releases containing bug and security fixes.
Before upgrading:
systemctl stop forgejo
Back up:
/etc/forgejo /var/lib/forgejo PostgreSQL database
Download and verify the appropriate newer Forgejo binary.
Then replace:
/usr/local/bin/forgejo
with the new binary.
Set permissions:
chmod 755 /usr/local/bin/forgejo
Start Forgejo:
systemctl start forgejo
Check:
systemctl status forgejo
And:
forgejo --version
Always review Forgejo’s upgrade documentation and release notes before jumping between major versions.
-
Production Security Recommendations
For an Internet-facing Forgejo installation, I recommend at minimum:
Keep Ubuntu updated
Regularly:
apt update apt upgrade
Do not expose PostgreSQL publicly
There should normally be no firewall rule allowing Internet access to:
5432/tcp
Do not expose Forgejo’s HTTP port
Don’t open:
3000/tcp
to the Internet.
Nginx should access Forgejo locally.
Disable public registration
For private deployments:
[service] DISABLE_REGISTRATION = true
Use SSH keys
Avoid password-based administrative SSH access where practical.
Install Fail2ban
For example:
apt install fail2ban
This can provide additional protection against repeated authentication attacks.
Maintain off-server backups
Back up:
PostgreSQL Forgejo repositories/data app.ini LFS objects attachments packages
and maintain copies outside the VPS.
-
Troubleshooting
Forgejo Returns 502 Bad Gateway
Check Forgejo:
systemctl status forgejo
Check port 3000:
ss -lntp | grep 3000
Test locally:
curl http://127.0.0.1:3000
Check logs:
journalctl -u forgejo -n 100 --no-pager
If local Forgejo works, inspect Nginx:
nginx -t
and:
journalctl -u nginx -n 100 --no-pager
Forgejo Won’t Start
Run:
journalctl -xeu forgejo
Common causes include:
- incorrect directory ownership;
- malformed
app.ini; - inaccessible database;
- incorrect database password;
- port 3000 already occupied;
- missing Git installation.
Check permissions:
ls -ld /var/lib/forgejo ls -ld /etc/forgejo ls -l /etc/forgejo/app.ini
Database Connection Failed
Test PostgreSQL directly:
psql -h 127.0.0.1 -U forgejo -d forgejo
Then verify the
[database]section:See Also: 🚀 Deploy ERPNext on Ubuntu VPS
grep -A10 '^\[database\]' /etc/forgejo/app.ini
Be careful when sharing this output for troubleshooting because it can contain the database password.
Clone URLs Show HTTP Instead of HTTPS
Check:
nano /etc/forgejo/app.ini
Make sure:
ROOT_URL = https://git.example.com/
Restart:
systemctl restart forgejo
Correct
ROOT_URLconfiguration is particularly important behind a reverse proxy because Forgejo uses it when generating URLs.SSH Clone Doesn’t Work
First make sure ordinary SSH works:
ssh git@git.example.com
Then verify port 22:
ss -lntp | grep ':22'
Check the firewall:
ufw status
And verify that the user’s SSH public key has actually been added in Forgejo.
Final Architecture
The resulting deployment looks like:
Internet
|
+----------+----------+
| |
HTTPS SSH
:443 :22
| |
v v
+-------+ +----------+
| Nginx | | OpenSSH |
+---+---+ +----+-----+
| |
127.0.0.1:3000 |
| |
+---------+----------+
|
v
+-----------+
| Forgejo |
| user: git |
+-----+-----+
|
+--------+--------+
| |
v v
+------------+ +-----------+
| PostgreSQL | | Git Repos |
| forgejo | | Git LFS |
+------------+ +-----------+
|
/var/lib/forgejo
Publicly exposed services are therefore limited primarily to:
22/tcp SSH / Git over SSH 80/tcp HTTP -> HTTPS redirect 443/tcp HTTPS / Forgejo
while these remain internal:
3000/tcp Forgejo HTTP 5432/tcp PostgreSQL
This gives you a clean production foundation: Ubuntu + Forgejo + PostgreSQL + Nginx + Let’s Encrypt, with systemd managing Forgejo and native SSH handling Git operations.
Once the basic installation is working, the next logical production additions are Forgejo Actions with a separate Runner, SMTP notifications, automated off-site backups, fail2ban rules tailored to Forgejo/Nginx, and optionally an object-storage backend for LFS, packages, attachments, and Actions artifacts. Forgejo provides separate administrative documentation for Actions, storage, reverse proxies, authentication, and other advanced deployment options.
Conclusion
You now know how to install Forgejo on Ubuntu VPS.









