Forgejo 16.0.4 and 15.0.8, released on 10 September 2026, fix a vulnerability the release notes rate critical: a malicious template repository could be used to read arbitrary data from the Forgejo host and execute arbitrary processes on it.
How it worked, per the release notes: when you create a repository from a template, Forgejo clones the template, removes its .git folder, expands template variables in the files listed in .forgejo/template, and then initialises a new git repository. The variable expansion could be abused to create a new .git folder, which git then adopted during initialisation. The fix removes any .git folder after expansion.
The same releases fix a second issue: API tokens restricted to certain repositories could modify branches outside their permission through the “allow maintainer edit” path on pull requests.
Who is exposed: any instance where someone who could create or import a malicious template repository is able to generate repositories from it: other users, open registration, or a compromised account. A single-user instance that never uses untrusted templates is at lower risk, but should update anyway.
Phase 1: Force a Manual Backup
Never pull a new Docker image or replace a binary without a backup. Database migrations fail. Data gets corrupted. You need a verified rollback path.
If you use SQLite, backing up is a simple file copy. Stop the Forgejo service first to prevent locked database writes.
sudo systemctl stop forgejo
# Or for Docker: docker compose stop forgejo
cp /var/lib/forgejo/data/forgejo.db /var/lib/forgejo/data/forgejo.db.bak_16.0.3
If you use PostgreSQL, do not copy the raw database files. You must create a SQL dump. Run this against your active Postgres container or local service:
docker exec -t forgejo-db pg_dump -U forgejo forgejo > /opt/backups/forgejo_pre_update.sql
Archive your custom configurations and repository data. The app.ini file dictates how your instance runs. If the update overwrites it, your server goes down.
tar -czvf /opt/backups/forgejo_data_pre_update.tar.gz /etc/forgejo /var/lib/forgejo
Phase 2: The Forgejo Update Docker Process
The fastest way to patch is via Docker. A standard forgejo update docker workflow involves pulling the specific release tag and recreating the container.
Do not use the :latest tag. Unpinned tags cause unexpected major version upgrades, breaking dependencies and database schemas without warning. Pin a specific version (at least 16.0.4, newer patch releases are better) or the 16 tag, which the Forgejo docs say tracks the latest 16.x release, if you trust minor updates.
Open your docker-compose.yml file and update the image tag:
services:
server:
image: codeberg.org/forgejo/forgejo:16.0.5
container_name: forgejo
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
networks:
- forgejo
volumes:
- /opt/forgejo/data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
security_opt:
- no-new-privileges:true
db:
image: postgres:16-alpine
restart: always
environment:
- POSTGRES_USER=forgejo
- POSTGRES_PASSWORD=your_secure_password
- POSTGRES_DB=forgejo
networks:
- forgejo
volumes:
- /opt/forgejo/postgres:/var/lib/postgresql/data
networks:
forgejo:
external: false
Pull the new image and recreate the stack. Docker replaces the running container with the patched version. The database remains intact on the mapped volume.
cd /opt/forgejo
docker compose pull
docker compose up -d
Check the logs immediately to confirm the database migration succeeded and the service is listening.
docker compose logs -f server
Look for the line indicating successful startup: Listen: http://0.0.0.0:3000. If you see a database schema mismatch error, restore your PostgreSQL dump and verify you did not skip a required major version upgrade.
Phase 3: Proxmox LXC Bare Metal Upgrade
If you installed Forgejo directly on a Debian or Ubuntu LXC container in Proxmox, you update by replacing the binary.
First, stop the running service.
sudo systemctl stop forgejo
Navigate to your installation directory and download the compiled binary (16.0.5 at the time of writing) from the official release repository. Verify your architecture. Most homelabs use amd64, but Raspberry Pi clusters require arm64.
cd /usr/local/bin
sudo wget -O forgejo-16.0.5 https://codeberg.org/forgejo/forgejo/releases/download/v16.0.5/forgejo-16.0.5-linux-amd64
Set the executable bit and replace the old symlink.
sudo chmod +x forgejo-16.0.5
sudo ln -sf forgejo-16.0.5 forgejo
Restart the systemd service.
sudo systemctl start forgejo
sudo systemctl status forgejo
Phase 4: Self-Hosted Git Security Homelab Hardening
Applying the patch fixes the immediate threat. You must still address the configuration flaws that made the exploit dangerous. Standard self-hosted git security homelab practices mitigate the blast radius of future vulnerabilities.
Disable Open Registration
Most homelab users host repositories for themselves or a small group of known collaborators. Open registration provides attackers a free authenticated account, bypassing unauthenticated exploit checks.
Edit your /etc/forgejo/app.ini (or the mapped Docker equivalent at /data/gitea/conf/app.ini):
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = true
Setting REQUIRE_SIGNIN_VIEW blocks unauthenticated users from browsing public repositories. This prevents information disclosure and thwarts automated scanners indexing your source code.
Implement Fail2Ban
Git servers face constant brute-force SSH and HTTP login attempts. Forgejo outputs structured logs for failed authentication. Map a fail2ban instance to read the Forgejo log directory and block offending IPs at the firewall level.
Create a specific filter for Forgejo in /etc/fail2ban/filter.d/forgejo.conf:
[Definition]
failregex = .*Failed authentication attempt for .* from <HOST>
.*Failed login for .* from <HOST>
ignoreregex =
Consider the rootless image
The standard Forgejo image starts as root and then drops to the git user, so it needs the capabilities for switching users: dropping all capabilities on it breaks startup. If you want the container to never run as root, use the rootless image documented by Forgejo: codeberg.org/forgejo/forgejo:16-rootless with user: 1000:1000, data under /var/lib/gitea and SSH on port 2222 inside the container. See the Forgejo Docker docs.
Restrict who can use templates
The vulnerability needed a malicious template repository. On a small instance, that’s another reason to disable open registration (above) and only trust templates from people you know.
Audit the Actions Runner
If you use Forgejo Actions, audit your runner configuration. The runner executes untrusted code by design. Never run the Forgejo runner binary directly on the host machine. Always use the Docker-in-Docker or isolated container approach. If an RCE breaks out of the runner environment, it should hit an isolated container boundary, not your hypervisor kernel.
Check your instance logs for signs of compromise prior to patching. Look for anomalous processes executing under the git user, unexpected outgoing network connections to unknown IP addresses, or newly created administrator accounts. If you suspect an intrusion, nuking the container and restoring from an older offline backup is the only safe remediation.
[discussion]
Comments are powered by Giscus — backed by GitHub Discussions. Sign in with GitHub to join the conversation.