Knowledge Base > Privacy & Self-Hosting > Migrating to Forgejo

Migrating from GitHub to Self-Hosted Forgejo

Your code, your server, your rules


Why Leave GitHub?

This is not about hating GitHub. GitHub is a great platform. It changed how the world collaborates on code. But the ownership and direction of the platform have changed, and those changes matter if you care about who has access to your work.

Here are the facts:

  • Microsoft acquired GitHub in 2018 for $7.5 billion. Since then, GitHub has been integrated deeper into Microsoft's ecosystem.
  • GitHub Copilot launched in 2021, trained on public repositories hosted on GitHub. This raised immediate questions about licensing and consent.
  • Copilot is now enabled by default on GitHub. As of April 24, 2026, GitHub's default setting allows Copilot to use repository content and user data for AI training and improvement. You can opt out, but the default is opt-in.
  • Your private repos live on Microsoft's infrastructure. You are trusting Microsoft's policies, security, and future decisions with your code. Those policies can change at any time.

None of this is conspiracy theory. It is documented in GitHub's own terms of service and Copilot settings pages. You can read the details at GitHub's Copilot policy documentation.

If you are fine with that, no judgment. But if you want to own your code infrastructure the same way you own your home network, read on.


What is Forgejo?

Forgejo is a self-hosted Git forge. Think of it as your own private GitHub that runs on your server. It is a community fork of Gitea, which itself was a fork of Gogs. Forgejo is maintained by the Codeberg community and is fully open source.

What you get:

  • Full Git repository hosting with a web interface
  • Issue tracking, pull requests, milestones, and labels
  • Repository migration tools (imports from GitHub, GitLab, Gitea, etc.)
  • User and organization management
  • API-compatible with Gitea and partially compatible with the GitHub API
  • Lightweight enough to run on minimal hardware
  • No telemetry, no tracking, no AI training on your code

What This Guide Covers

  1. Server prerequisites and setup
  2. Docker and Docker Compose installation
  3. UFW firewall configuration
  4. SSH hardening
  5. Forgejo installation via Docker Compose
  6. Initial Forgejo configuration
  7. Importing repositories from GitHub
  8. Updating your local Git remotes
  9. Backup strategy
  10. Maintenance and upgrades

Prerequisites

Server Requirements

You need a Linux server. This can be a dedicated machine, a virtual machine, or an LXC container. This guide uses Ubuntu 24.04 LTS Server, but any Debian-based distro will work with minor adjustments.

ResourceMinimumRecommended
CPU1 vCPU2 vCPUs
RAM2 GB4 GB
Disk20 GB40 GB+
OSUbuntu 24.04 LTS Server (or similar)

This guide assumes you already have a server running with SSH access and sudo privileges. If you need help setting up a VM, see our Proxmox guide for the general VM creation process, or refer to your hosting provider's documentation.

Proxmox Users

If you are running this inside an LXC container, you must enable the nesting feature on the container. Without it, Docker will not work inside LXC. In the Proxmox web UI: select your container > Options > Features > set nesting=1.

Network Requirements

  • A static IP address for your server (set via DHCP reservation or static config)
  • Ports 3000 (web UI) and 222 (Git SSH) available on your LAN
  • Outbound internet access (for Docker image pulls and system updates)

Throughout this guide, example IPs use 192.168.1.100 for the Forgejo server and 192.168.1.0/24 for the local network. Replace these with your actual values.

On Your Workstation

  • Git installed
  • SSH key pair (for Git-over-SSH)
  • A web browser (for the Forgejo web UI and GitHub token generation)

Step 1: Prepare the Server

Update and Install Essentials

sudo apt update && sudo apt upgrade -y

sudo apt install -y \
  ca-certificates \
  curl \
  gnupg \
  lsb-release \
  git \
  git-lfs \
  htop \
  net-tools \
  unattended-upgrades \
  apt-listchanges

Configure Automatic Security Updates

sudo dpkg-reconfigure -plow unattended-upgrades

Select Yes when prompted. Verify with:

cat /etc/apt/apt.conf.d/20auto-upgrades

Expected output:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

Set Your Timezone

sudo timedatectl set-timezone YOUR_TIMEZONE

Replace YOUR_TIMEZONE with your timezone (e.g., America/New_York, America/Chicago, Europe/London). Verify with timedatectl.

Update /etc/hosts

sudo nano /etc/hosts

Add your server's hostname and IP:

127.0.0.1       localhost
127.0.1.1       forgejo

192.168.1.100   forgejo

Replace 192.168.1.100 with your server's actual IP. Save and exit (Ctrl+O, Enter, Ctrl+X).

Reboot if Kernel Was Updated

sudo reboot

Step 2: Install Docker

Add Docker's Official GPG Key and Repository

sudo install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install Docker Engine and Compose

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

Configure Permissions

sudo usermod -aG docker $USER

Log out and back in for the group change to take effect.

Enable Docker on Boot

sudo systemctl enable docker
sudo systemctl enable containerd

Verify

docker --version
docker compose version
docker run hello-world

You should see version info and a "Hello from Docker!" message.


Step 3: Configure Firewall

This is a private server. Only your local network should be able to reach it. No services are exposed to the internet.

Set Default Policies

sudo ufw default deny incoming
sudo ufw default allow outgoing

Allow SSH

sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp comment 'SSH from LAN'

Allow Forgejo Web UI (Port 3000)

sudo ufw allow from 192.168.1.0/24 to any port 3000 proto tcp comment 'Forgejo Web UI from LAN'

Allow Forgejo Git SSH (Port 222)

sudo ufw allow from 192.168.1.0/24 to any port 222 proto tcp comment 'Forgejo Git SSH from LAN'

Enable UFW

sudo ufw enable

Type y to confirm.

Verify

sudo ufw status verbose

You should see all three rules scoped to your LAN subnet. No "Anywhere" rules.

Do Not Lock Yourself Out

After enabling UFW, open a new terminal and verify you can still SSH in before closing your existing session. If you get locked out, use your hypervisor's console (Proxmox, VMware, etc.) to fix the rules.

Optional: Disable IPv6 in UFW

If you are not using IPv6 on your network:

sudo nano /etc/default/ufw

Change IPV6=yes to IPV6=no, then reload:

sudo ufw reload

Step 4: Harden SSH

Copy Your SSH Key to the Server

From your workstation:

ssh-copy-id YOUR_USERNAME@192.168.1.100

Test key-based login:

ssh YOUR_USERNAME@192.168.1.100

You should connect without a password prompt.

Lock Down the SSH Daemon

Only do this after confirming key-based auth works.

sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

Add:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
AllowUsers YOUR_USERNAME

Replace YOUR_USERNAME with your actual username.

sudo systemctl restart ssh

Test from a new terminal before closing your current session.


Step 5: Install Forgejo

Create the Directory

sudo mkdir -p /opt/forgejo
sudo chown $USER:$USER /opt/forgejo

Create the Docker Compose File

nano /opt/forgejo/docker-compose.yml

Paste the following:

networks:
  forgejo:
    external: false

services:
  server:
    image: codeberg.org/forgejo/forgejo:14
    container_name: forgejo
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - FORGEJO__server__DOMAIN=192.168.1.100
      - FORGEJO__server__SSH_DOMAIN=192.168.1.100
      - FORGEJO__server__ROOT_URL=http://192.168.1.100:3000/
      - FORGEJO__server__SSH_PORT=222
      - FORGEJO__server__SSH_LISTEN_PORT=22
    restart: always
    networks:
      - forgejo
    volumes:
      - ./forgejo-data:/data
      - /etc/localtime:/etc/localtime:ro
    ports:
      - '3000:3000'
      - '222:22'

Replace every instance of 192.168.1.100 with your server's actual IP address. Save and exit.

Start Forgejo

cd /opt/forgejo
docker compose up -d

Verify

docker compose ps

You should see a container named forgejo with status Up and ports 0.0.0.0:222->22/tcp and 0.0.0.0:3000->3000/tcp.

Check logs for errors:

docker compose logs -f

Press Ctrl+C to stop following.


Step 6: Configure Forgejo

Open your browser and go to:

http://YOUR_SERVER_IP:3000

You will see the Forgejo initial configuration page. Most defaults are fine since we set environment variables in Docker Compose. Verify the following:

Database Settings

SettingValue
Database TypeSQLite3
Path/data/gitea/forgejo.db

SQLite is more than enough for personal or small-team use. No external database needed.

General Settings

SettingValue
Site TitleYour choice (e.g., "Forgejo")
Server DomainYOUR_SERVER_IP
SSH Server Port222
HTTP Listen Port3000
Base URLhttp://YOUR_SERVER_IP:3000/

Administrator Account

Create your admin account with a strong password. The first registered user automatically becomes the administrator.

Click Install Forgejo. After installation, log in with the admin account you just created. You should see a clean dashboard with your profile, an empty repository list, and an activity feed.


Step 7: Import Repositories from GitHub

Generate a GitHub Personal Access Token

You need a temporary token to pull your repos from GitHub.

  1. Go to github.com/settings/tokens
  2. Click Generate new token > Generate new token (classic)
  3. Name it something like Forgejo Migration
  4. Set expiration to 7 days (you only need it temporarily)
  5. Select the repo scope (full control of private repositories)
  6. Click Generate token and copy it immediately

Import a Repository

  1. In Forgejo, click the + button (top right) > New Migration
  2. Select GitHub as the source
  3. Fill in:
    • Clone Address: https://github.com/yourusername/your-repo.git
    • Access Token: Paste your GitHub token
    • Repository Name: Same as original (or rename)
    • Migration Items: Check all that apply (Topics, Milestones, Labels, Issues, Pull Requests, Releases)
  4. Click Migrate Repository

Repeat for each repository you want to migrate.

Verify Each Import

  • Commit history is complete
  • All branches are present
  • Files are intact
  • Issues and labels imported correctly (if applicable)
Clean Up

After migration is complete, go back to GitHub and delete the personal access token. You do not need it anymore and it should not be left active.


Step 8: Update Your Local Git Remotes

After importing your repos to Forgejo, point your local clones to the new server.

Check Your Current Remote

cd /path/to/your/repo
git remote -v

You will see something like:

origin  https://github.com/yourusername/repo.git (fetch)
origin  https://github.com/yourusername/repo.git (push)

Update the Remote

Option A: HTTP (simpler, uses password/token auth)

git remote set-url origin http://192.168.1.100:3000/yourusername/repo.git

Option B: SSH (uses SSH key auth, recommended)

git remote set-url origin ssh://git@192.168.1.100:222/yourusername/repo.git

Add Your SSH Key to Forgejo (for SSH)

  1. Copy your public key: cat ~/.ssh/id_ed25519.pub (or id_rsa.pub)
  2. In Forgejo, go to Settings > SSH / GPG Keys
  3. Click Add Key, paste your key, give it a name
  4. Click Add Key

Verify

git remote -v
git fetch
git push

Everything should work the same as it did with GitHub.


Backup Strategy

What to Back Up

The Forgejo data directory contains everything:

/opt/forgejo/forgejo-data/

This includes all Git repositories, the SQLite database, configuration files, and any uploaded assets.

Application-Level Backup

Create a backup script:

nano /opt/forgejo/backup.sh
#!/bin/bash
# Forgejo Backup Script
BACKUP_DIR="/opt/forgejo/backups"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"

# Use Forgejo's built-in dump command
docker exec forgejo /usr/local/bin/forgejo dump \
  -c /data/gitea/conf/app.ini \
  --file "/data/forgejo-dump-${DATE}.zip"

# Move the dump out of the container volume
mv /opt/forgejo/forgejo-data/forgejo-dump-${DATE}.zip "$BACKUP_DIR/"

# Clean up backups older than 30 days
find "$BACKUP_DIR" -name "forgejo-dump-*.zip" -mtime +30 -delete

echo "Backup complete: $BACKUP_DIR/forgejo-dump-${DATE}.zip"

Make it executable and schedule it:

chmod +x /opt/forgejo/backup.sh

Add a daily cron job (runs at 2:00 AM):

crontab -e

Add this line:

0 2 * * * /opt/forgejo/backup.sh >> /var/log/forgejo-backup.log 2>&1

If your server runs on a hypervisor like Proxmox with a guest agent installed, you can also use hypervisor-level backups for full VM snapshots with filesystem consistency.


Maintenance and Upgrades

Updating Forgejo

The :14 tag in your compose file tracks the latest patch release in the v14.x line. To update:

cd /opt/forgejo
docker compose pull
docker compose down
docker compose up -d

Verify the update by checking the version in the startup logs:

docker compose logs | head -20

Major Version Upgrades

Major upgrades (e.g., v14 to v15) require a manual version bump:

  1. Read the release notes at forgejo.org/releases
  2. Back up your data first
  3. Update the image tag in docker-compose.yml (e.g., change :14 to :15)
  4. Pull and restart with the commands above
  5. Check logs for migration messages and verify the web UI

Updating the Host OS

sudo apt update && sudo apt upgrade -y

Reboot if a kernel update was applied. Docker and Forgejo will restart automatically thanks to the restart: always policy.


Troubleshooting

Forgejo Will Not Start

cd /opt/forgejo
docker compose logs -f

Common issues:

  • Port conflict: Another service is using port 3000 or 222. Check with sudo ss -tlnp | grep -E '3000|222'
  • Permission issue: The forgejo-data directory must be owned by UID/GID 1000. Fix with sudo chown -R 1000:1000 /opt/forgejo/forgejo-data
  • Disk full: Check with df -h

Cannot Access Web UI

  • Verify container is running: docker compose ps
  • Verify firewall: sudo ufw status
  • Test from the server itself: curl http://localhost:3000
  • Check if port is listening: sudo ss -tlnp | grep 3000

Cannot Push or Pull via SSH

  • Verify port 222 is open: sudo ss -tlnp | grep 222
  • Test connectivity: ssh -T git@YOUR_SERVER_IP -p 222
  • Verify your SSH key is added in Forgejo's web UI under Settings > SSH / GPG Keys
  • Check the URL format: ssh://git@YOUR_SERVER_IP:222/username/repo.git

Cannot Push or Pull via HTTP

  • Verify remote URL: git remote -v
  • Test with: git ls-remote http://YOUR_SERVER_IP:3000/username/repo.git
  • You may need an access token for HTTP auth: go to Settings > Applications > Generate New Token

Quick Reference

Service Management

ActionCommand
Startcd /opt/forgejo && docker compose up -d
Stopcd /opt/forgejo && docker compose down
Restartcd /opt/forgejo && docker compose restart
Logscd /opt/forgejo && docker compose logs -f
Updatecd /opt/forgejo && docker compose pull && docker compose down && docker compose up -d
Statuscd /opt/forgejo && docker compose ps

Key Paths

PathPurpose
/opt/forgejo/Project root
/opt/forgejo/docker-compose.ymlDocker Compose config
/opt/forgejo/forgejo-data/All data (repos, DB, config)
/opt/forgejo/backups/Application backups

Git Remote Formats

ProtocolFormat
HTTPhttp://YOUR_SERVER_IP:3000/username/repo.git
SSHssh://git@YOUR_SERVER_IP:222/username/repo.git

Resources