Why the August 17, 2026 outage changes the question
On August 17, 2026, at 9:40 a.m. ET, GitHub experienced a capacity failure in its Central US datacenter. The platform hit a new traffic peak its infrastructure could not absorb: roughly a 20% error rate on web experiences and API calls, up to 50% on archive downloads. GitHub Actions was among the most durably affected services. Full service did not return until 5:27 p.m. ET — 7 hours and 47 minutes after the incident began — publicly acknowledged by GitHub on August 20, 2026.
DanubeData, a data infrastructure company, published a detailed post-mortem: the incident exposed a hidden dependency of their control plane on GitHub. In response, they migrated 9 repositories — 333 pull requests and roughly 400 MB of Git history — to a self-hosted Forgejo instance in a single day. Their main build pipeline and production deployment workflow followed the same day.
The question is no longer "can GitHub go down?" but "what is our exposure if it does?". Forgejo Actions answers that question by moving CI/CD execution to your own infrastructure.
Benefits of a self-hosted Forgejo for your CI/CD
- Availability independent of GitHub — a platform outage does not interrupt your builds or deployments.
- Compatible YAML syntax — the majority of GitHub Actions workflows run without modification on Forgejo Actions.
- Fixed and predictable cost — every pipeline minute is a resource already paid for on your VPS, not an additional line item billed per minute.
- Pipeline confidentiality — source code, environment secrets and build artifacts never leave your infrastructure.
- Configurable runners — run jobs in a Docker container, a native process or an LXC environment unavailable on GitHub.
- Nonprofit governance — Forgejo is maintained by Codeberg e.V., with no paid enterprise edition and no risk of a commercial pivot.
Prerequisites
Before starting the installation, verify that your VPS meets the following requirements.
RAM: 2 GB minimum for the forge alone, with 5 to 20 developers and reasonably sized repositories. Plan for 4 GB if you run Forgejo Actions with runners that compile locally (Go, Rust, Java).
vCPU: 2 vCPU is sufficient for a team of 20 developers. Add 2 vCPU per concurrent runner for CPU-intensive builds.
Disk: reserve at least 20 GB of SSD for repositories, Git history, CI artifacts and logs. Plan for 50 GB if you host several years of history or large binary artifacts.
Network: port 22 remains available on the host (system SSH); Forgejo SSH will be exposed on a separate port, conventionally 2222. Port 443 must be open for the reverse proxy.
Domain: prepare a subdomain such as git.yourdomain.com with an A record pointing to your VPS IP.
Software: Docker 24+ and the Compose plugin installed on the VPS.
Migrating from GitHub to Forgejo Actions: installation and porting
Install Forgejo with Docker and PostgreSQL
Create the working directory /opt/forgejo and a data subdirectory. Write a docker-compose.yml with two services: forgejo (image codeberg.org/forgejo/forgejo:latest, internal port 3000, SSH mapped on 2222:22, volume ./data:/data, variables USER_UID=1000 and USER_GID=1000) and db (image postgres:16, dedicated volume, variables POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB). Run docker compose up -d and watch docker compose logs -f forgejo until you see HTTP port 3000 active.
Configure the reverse proxy and SSL certificate
With Caddy, a single block is enough: git.yourdomain.com { reverse_proxy localhost:3000 }. The Let's Encrypt certificate is provisioned automatically. With Nginx, create a server block listening on 443 with proxy_pass http://127.0.0.1:3000; and add mandatorily proxy_read_timeout 600s; — DanubeData hit a 504 without this setting, which halted the import at 196 of 333 pull requests. Redirect HTTP to HTTPS in a separate block.
Complete the initial setup
Go to https://git.yourdomain.com. The setup wizard asks for: database driver (PostgreSQL), host (db:5432), credentials, HTTPS base URL and external SSH port (2222). Create the administrator account. For a private instance, disable self-registration in the settings or by adding DISABLE_REGISTRATION=true to the service environment variables.
Migrate GitHub repositories
In the Forgejo interface, click New repository → Migrate. Select GitHub as the source. Enter the repository URL (https://github.com/your-org/your-repo), a GitHub personal access token with the repo scope, and enable migration of pull requests, labels, milestones and releases. For large repositories (> 1 GB or > 200 pull requests), test first with the reverse proxy timeout at 600 s. Delete the incomplete repository and retry if a 504 occurs. Repeat for each repository.
Register a Forgejo Actions runner
In Forgejo, go to Administration → Actions → Runners → Create runner. Copy the registration token. Add an act-runner service to your docker-compose.yml with the image codeberg.org/forgejo/runner:latest, and set the variables FORGEJO_INSTANCE_URL (HTTPS URL of your instance) and FORGEJO_RUNNER_REGISTRATION_TOKEN. Run docker compose up -d act-runner. Verify that the runner appears as Online in the administration panel.
Place workflow files in the correct directory
On GitHub, your workflows live in .github/workflows/. On Forgejo, they must be in .forgejo/workflows/. Rename the directory in each migrated repository and push the change. The rest of the YAML file — on: triggers, jobs, steps, uses: for compatible third-party actions — remains identical in most cases. Forgejo does not read the .github/ directory.
Adapt GitHub-specific elements
A few adaptation points: replace actions that call the GitHub API (gh-based) with a Forgejo equivalent or remove them; remove any permissions: id-token: write (Forgejo uses enable-openid-connect in the workflow file for OIDC); if your workflow references a tool absent from the runner's Debian bookworm image, add an apt-get install -y <tool> step. The actions/checkout and actions/setup-node actions are resolved natively.
Validate the first pipelines
Create a minimal workflow .forgejo/workflows/smoke.yml triggered on push, with a single job running echo "Pipeline OK". Check Actions in the Forgejo interface. Once this first run is green, migrate your production workflows one by one and fix any discrepancies along the way.
Post-migration configuration
After validating pipeline execution, three items remain to configure.
Webhooks and notifications: if external systems (Slack, Mattermost, a deployment webhook) were listening for GitHub events, recreate them in Forgejo under Settings → Webhooks. The payload is similar to GitHub's but differs on a few fields — adjust your receiving endpoints accordingly.
Pipeline secrets: GitHub secrets (menu Settings → Secrets) must be recreated in Forgejo under Repository Settings → Actions → Secrets. For organization secrets, use Administration → Organizations → [your org] → Settings → Actions → Secrets. Never migrate a secret by embedding its value in a commit or a versioned file.
Runner permissions: by default, a runner registered at instance level can execute workflows for all repositories. For finer control, restrict a runner to a specific organization or repository in the Runners tab of the corresponding settings.
Hardening your Forgejo instance
Three measures significantly reduce the attack surface of an instance exposed to the internet.
Enable mandatory 2FA for all accounts, especially administrator accounts: Administration → Authentication Settings → Require 2FA.
Audit registered SSH keys. A user migrated from GitHub may have orphaned or outdated keys in their profile. Ask each team member to review their keys under Settings → SSH/GPG Keys and delete any that no longer correspond to an active device.
Enable HMAC verification on all outgoing webhooks. In each webhook, fill in the Secret field with a random string of at least 32 characters. The receiving endpoint must verify the X-Gitea-Signature header (identical on Forgejo). Without this check, anyone who knows your endpoint URL can trigger a deployment action.
Troubleshooting — common errors
504 Gateway Timeout during repository migration. Forgejo performs the import synchronously within the HTTP request. If the reverse proxy cuts the connection before the import finishes, the migration stops and the repository remains stuck as "Migration in progress". Increase proxy_read_timeout to 600 s (Nginx) or add timeouts { read_body 10m } (Caddy) before starting the migration. Then delete the incomplete repository and retry.
Runner stays Offline after startup. Verify that FORGEJO_INSTANCE_URL points to the public HTTPS URL of your instance, not localhost or the container's internal IP. The runner must reach Forgejo via the same path as an external browser.
Workflow not triggered after push. Verify that the workflow files are in .forgejo/workflows/ and not in .github/workflows/. Forgejo does not read the .github/ directory.
Error: this step uses an action, but the runner does not support actions. Some third-party actions referenced by uses: call the GitHub API. Replace them with an equivalent available on Codeberg or mirror them to your instance.
permission denied on a script at the end of a job. The runner's base image (Debian bookworm) does not include the same utilities as GitHub's Ubuntu image. Add an apt-get install -y <tool> step at the beginning of the job, or specify a custom Docker image via container:.
Further reading
This guide covers installation and migration of your CI/CD to Forgejo Actions. To go further, the following articles cover related topics: initial Forgejo hosting on a VPS with Docker and SSL, setting up a Woodpecker CI pipeline alongside Forgejo, and migrating your GitHub repositories to a self-hosted Git forge.
Host Forgejo on your own VPS — complete installation, reverse proxy and first runner.
Woodpecker CI pipeline on VPS with Forgejo — an alternative to Forgejo Actions for teams that want to separate the forge and the CI engine.
Migrate your GitHub repositories to Gitea or Forgejo on a VPS — step-by-step migration procedure using the official tool.