Forgejo is a lightweight, self-hostable Git service that emerged from a fork of the Gitea project. While providing a centralized code repository for development teams, its integrated Forgejo Actions feature enables the automation of CI/CD (Continuous Integration/Continuous Delivery) processes directly within the Git platform. In this post, we will walk through how to install and configure Forgejo and Actions on your own server and optimize your software development processes.
Setting up your own Git and CI platform offers significant advantages, especially in terms of data sovereignty, customization flexibility, and cost control. Forgejo provides a highly suitable solution to meet this need.
Why Forgejo and Forgejo Actions?
Reliance on external Git and CI/CD providers can sometimes lead to issues such as data sovereignty concerns, security policy limitations, or customization restrictions. For organizations working with sensitive data or subject to strict regulations, maintaining control over their codebase and CI/CD processes is critical. Forgejo is a lightweight and fast alternative written in Go, developed to provide this control.
Forgejo Actions, inspired by GitHub Actions, is an automation engine directly integrated into Forgejo. This allows you to host your code and manage processes like building, testing, and deployment all within the same platform. This integration simplifies the development workflow and reduces the toolchain.
ℹ️ Pragmatic Approach
Self-hosting may not always be the best solution. However, if there are specific needs (data security, cost optimization, high customization requirements), solutions like Forgejo offer a very powerful option. It provides a platform that can be quickly set up, especially for small and medium-sized teams.
In some corporate projects, keeping code and data off-premises is not a preference but a necessity. Forgejo offers a single, lightweight solution that can be used as both a Git server and an integrated CI/CD tool in such scenarios. This significantly reduces the burden of setting up and managing separate Git and CI/CD servers.
Forgejo Installation: Basic Steps
One of the most practical and flexible ways to install Forgejo is by using Docker and docker-compose. This method simplifies dependency management and makes the installation repeatable. Before starting, ensure that Docker and docker-compose are installed on your server and that you have a domain name for Forgejo (e.g., git.domain.com) along with its SSL/TLS certificates (e.g., from Let's Encrypt).
We begin by creating a docker-compose.yml file. This file will include the Forgejo service, a PostgreSQL database, and an Nginx reverse proxy. Nginx will handle SSL termination and route incoming requests to Forgejo.
# docker-compose.yml
version: "3"
services:
forgejo:
image: codeberg.org/forgejo/forgejo:16 # Updated to current stable version 16.0.0.
container_name: forgejo
restart: always
environment:
- DB_TYPE=postgres
- DB_HOST=db:5432
- DB_USER=forgejo
- DB_PASS=your_db_password
- DB_NAME=forgejo
- FORGEJO__server__DOMAIN=git.domain.com # GITEA__ prefixes updated to FORGEJO__.
- FORGEJO__server__SSH_DOMAIN=git.domain.com
- FORGEJO__server__SSH_PORT=2222 # Port Forgejo will use for SSH
- FORGEJO__server__HTTP_PORT=3000
- FORGEJO__server__ROOT_URL=https://git.domain.com/
- FORGEJO__service__DISABLE_REGISTRATION=false # Can be set to true if desired
- FORGEJO__actions__ENABLED=true # Correct variable to enable Actions runners.
# - GITEA__RUNNER__DISABLE_BUILTIN_ACTIONS=false # Removed as there is no direct equivalent in Forgejo documentation.
volumes:
- ./forgejo-data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000" # For Nginx access only
- "127.0.0.1:2222:22" # For SSH, Nginx will not use this
depends_on:
- db
db:
image: postgres:15 # PostgreSQL 15 is a current and stable version.
container_name: forgejo_db
restart: always
environment:
- POSTGRES_USER=forgejo
- POSTGRES_PASSWORD=your_db_password
- POSTGRES_DB=forgejo
volumes:
- ./postgres-data:/var/lib/postgresql/data
nginx:
image: nginx:stable-alpine
container_name: forgejo_nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- forgejo
After creating this docker-compose.yml file, you need to prepare an Nginx configuration file (nginx/nginx.conf) and your SSL certificates (nginx/ssl/fullchain.pem, nginx/ssl/privkey.pem). The Nginx configuration typically involves routing incoming HTTPS requests to port 3000 of the Forgejo container. For example:
# nginx/nginx.conf
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name git.domain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name git.domain.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
location / {
proxy_pass http://forgejo: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;
}
}
}
Once these files are prepared, you can start all services with the docker-compose up -d command. On the first run, you will need to access Forgejo via the web interface and create the first administrator account. Ensure that the database connection details match those in your docker-compose.yml file.
Preparing the Forgejo Actions Environment
To use Forgejo Actions, we need "runners" to execute workflows. Runners work similarly to self-hosted runners in GitHub Actions. By setting up one or more runners on your own server, you can run Forgejo Actions jobs on these machines. This is ideal for jobs that require specific hardware (like GPUs) or network isolation.
To set up a runner, first log in to the Forgejo web interface. You can add a new runner from the admin panel or from the "Settings > Actions > Runners" section in a repository. Here, you will be provided with a registration token and commands to set up the runner.
On the machine where you will set up the runner (this can be the same server as Forgejo or a different one), follow these steps:
-
Download the Runner Binary: Download the appropriate
forgejo-runnerbinary for your operating system according to the instructions provided by Forgejo. For example, for Linux:
# Check the latest stable version and update the download URL. # The latest version of forgejo-runner is v16.0.0. wget https://codeberg.org/forgejo/forgejo-runner/releases/download/v16.0.0/forgejo-runner-linux-amd64 # Version updated to v16.0.0. mv forgejo-runner-linux-amd64 forgejo-runner chmod +x forgejo-runnerRemember to replace the version number with the latest version recommended by your Forgejo instance.
-
Register the Runner: Register the runner with Forgejo using the downloaded binary. This command will prompt you for information such as the Forgejo URL, registration token, and labels you want to assign to the runner (e.g.,
linux,amd64,docker).
./forgejo-runner registerOnce these steps are complete, the runner will appear as active in your Forgejo instance.
-
Run the Runner as a Service: To ensure the runner runs continuously, configuring it as a
systemdservice is the best approach. Asystemdunit file ensures the runner starts automatically and restarts in case of failure.
# /etc/systemd/system/forgejo-runner.service [Unit] Description=Forgejo Actions Runner After=network.target [Service] Type=simple User=runner # It is recommended to create a separate 'runner' user for the runner. ExecStart=/opt/forgejo-runner/forgejo-runner daemon Restart=always WorkingDirectory=/opt/forgejo-runner [Install] WantedBy=multi-user.targetAfter creating this file, move the runner binary to the
/opt/forgejo-runner/directory, create a user namedrunner, and grant the necessary permissions. Then, start the service withsudo systemctl enable forgejo-runnerandsudo systemctl start forgejo-runnercommands.
⚠️ Runner Security
Runners pose potential security risks as they execute your code. It is important to run runners in separate virtual machines or isolated Docker environments. Using cgroup limits for resource constraints can prevent runners from consuming system resources, especially in shared environments.
Don't forget to check the status of your runners from the Forgejo web interface to ensure they are healthy. You can add multiple runners to distribute the workload and increase parallel execution.
Creating Your First Forgejo Actions Workflow
Forgejo Actions workflows are defined as YAML files under the .forgejo/workflows directory in your repository. This structure is largely similar to GitHub Actions, meaning you can leverage your existing GitHub Actions knowledge. A workflow file defines one or more jobs that will be triggered by specific events (e.g., push, pull request) and the steps to be executed within each job.
Let's create a simple test workflow for an example Python project. This workflow will be triggered on every push event, set up the Python environment, install dependencies, and run tests.
# .forgejo/workflows/python_test.yml
name: Python Test and Build
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
jobs:
build_and_test:
runs-on: linux # Enter your runner's label here, e.g.: self-hosted, linux, docker.
steps:
- name: Checkout Code
uses: actions/checkout@v6 # actions/checkout@v6 updated to current version.
- name: Set up Python Environment
uses: actions/setup-python@v5 # Available and current version on data.forgejo.org.
with:
python-version: '3.9'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Tests
run: |
pytest
- name: Build Application (Example)
run: |
echo "Building application..."
# A real build command could go here
# python setup.py sdist bdist_wheel
- name: Store Compiled Files (Artifact)
uses: actions/upload-artifact@v4 # Available and current version on data.forgejo.org.
with:
name: my-app-dist
path: dist/ # Assumed directory for build output
The core concepts used in this example are:
-
name: The display name of the workflow. -
on: Determines when the workflow will be triggered.pushandpull_requestare the most common. You can target specific branches. -
jobs: Logical units that will run in parallel or sequentially within the workflow. Each job must have its ownruns-onproperty. -
runs-on: Specifies which runner the job will run on. You can use general labels likedockeror custom labels you've given to your runner (self-hosted,my-custom-runner). -
steps: Commands or actions that will run sequentially within a job. -
name: The display name of the step. -
uses: To use an existing Actions action.actions/checkout@v6is used to checkout code,actions/setup-python@v5to set up the Python environment. -
run: To execute shell commands directly on the runner. -
with: To pass parameters to the action or command being used.
💡 Fast Feedback
Running tests that can fail quickly in the initial steps of your CI/CD workflows provides faster feedback to developers. For example, placing static code analysis or linter checks at the beginning is a good strategy to catch basic errors before proceeding to lengthy integration tests.
When you add this workflow file to your repository and push it, Forgejo Actions will automatically trigger and run the defined steps on the runner. You can track the status and logs of the process from the "Actions" tab in the Forgejo interface.
Advanced Forgejo Actions Scenarios and Tips
Forgejo Actions offers advanced features to support more complex CI/CD scenarios beyond simple build and test processes. These include dependency caching, matrix strategies, secret management, and custom deployment mechanisms. These features allow you to make your workflows more efficient, flexible, and secure.
Dependency Caching
Re-downloading dependencies (npm modules, pip packages, etc.) every time a workflow runs can be time-consuming. Forgejo Actions can speed up subsequent runs by caching these dependencies with the actions/cache action.
# Example dependency caching step
- name: Load Cache (Pip)
uses: actions/cache@v4 # Available and current version on data.forgejo.org.
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install Dependencies
run: pip install -r requirements.txt
This step creates a cache key based on the hash of the requirements.txt file. If the key matches, it restores from the cache; otherwise, it installs dependencies and creates a new cache. This significantly reduces CI times, especially for projects with large dependency sets.
Matrix Strategies
Matrix strategies are very useful when you need to run the same job in different environments (e.g., different Python versions, operating systems). This allows you to test multiple combinations with a single workflow definition.
jobs:
test:
runs-on: linux # Enter your runner's label here.
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10']
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: pytest
This matrix will run the test job separately for each Python version.
Secrets Management
Writing sensitive information such as database connection strings, API keys, or SSH keys directly into your workflow files poses a security risk. Forgejo allows you to define secrets at the repository or organization level. These secrets can be used in your workflows as secrets.MY_SECRET.
🔥 Protecting Sensitive Information
Secrets are never displayed as plain text in workflow logs. However, be careful not to accidentally print these variables with an
echocommand. Also, you should carefully manage the runners' access to this sensitive information; an unauthorized runner should be prevented from accessing sensitive data.
Deployment Scenarios
Deployment with Forgejo Actions typically involves connecting to a server via SSH and running commands, or transferring files with tools like rsync. You can define an SSH key as a secret, and then use this key on the runner to access the target server and run deployment commands.
# Example deployment step
- name: Deploy Application to Server
env:
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# CAUTION: StrictHostKeyChecking=no carries security risks and should only be used in secure, controlled environments.
# In production environments, known host keys should be used or a more secure authentication method should be preferred.
# This command connects to 'your-server.com' as user 'user' on the remote server.
# It is recommended to always backup before deployment and consider dry-run options.
ssh -o StrictHostKeyChecking=no user@your-server.com "cd /var/www/my-app && git pull && systemctl restart my-app.service"
This example connects to the remote server using an SSH key and runs deployment commands there. For more complex deployment scenarios, you can run automation tools like Ansible or Fabric on the runner.
These advanced scenarios transform Forgejo Actions from just a testing tool into a full-fledged CI/CD platform. The flexibility provided by self-hosting allows you to tailor these processes to your specific needs.
Maintenance, Security, and Performance Optimization
Setting up a self-hosted Git and CI/CD platform brings with it ongoing responsibilities for maintenance, security management, and performance optimization. These aspects are critical to ensuring the system runs stably, securely, and efficiently.
Ongoing Maintenance
It is important to keep Forgejo and its runners regularly updated. New versions typically include bug fixes, security patches, and performance improvements.
- Forgejo Updates: Pulling the Docker image and re-running
docker-compose up -dis usually sufficient. However, for major version upgrades, database schema changes may occur, so carefully reading the update notes and taking backups is vital. - Runner Updates: You may need to download the runner binary, replace the existing one, and restart the
systemdservice. - Backup: Regularly backing up the Forgejo database (PostgreSQL) and the
forgejo-datafolder is fundamental to your disaster recovery plan. Creating automated backup scripts and storing backups in a different storage location is a best practice.
Security Management
Like any service running on your own server, Forgejo and its runners must be managed carefully from a security perspective.
- Network Segmentation: Keeping Forgejo and runners in separate VLANs if possible provides network-level isolation. Defining firewall rules that only allow runners to communicate with Forgejo is important.
- Access Control: Ensure that only authorized personnel can access the Forgejo panel. Restrict SSH access to the servers where runners are running and implement strong password/key policies.
- CVE Tracking: Monitoring CVE notifications for the core components used by Forgejo (Go runtime, PostgreSQL, Docker) and runners helps in early detection of potential security vulnerabilities. System-level hardening steps like kernel module blacklisting can also enhance overall security.
- Audit Logs: Regularly monitoring Forgejo's and the server's audit logs helps detect suspicious activity. You can track system events with
journaldorauditd.
Performance Optimization
Performance optimization is crucial for your workflows to run quickly and efficiently.
- Database Tuning: PostgreSQL is the backbone of Forgejo. Adjusting parameters like
shared_buffers,work_memin thepostgresql.conffile according to your server's resources significantly impacts database performance. Monitoring WAL bloat and performing regularVACUUMoperations are also important. - Runner Resources: Ensure runners have sufficient CPU, RAM, and disk I/O. Especially if many workflows are running concurrently, it may be necessary to scale runners or use more powerful machines. You can prevent runners from overusing system resources by using cgroup limits.
- Caching: As mentioned above, dependency caching plays a big role in reducing workflow durations.
- Parallel Execution: Logically dividing your workflows to run in parallel reduces the overall CI/CD time. Matrix strategies can help with this.
- Disk Performance: The performance of the disks where Docker containers run directly affects build and test times. Using fast SSDs makes a difference in such I/O-intensive workloads.
When self-hosting Forgejo, not overlooking these maintenance, security, and performance considerations will ensure you have a more robust and reliable platform in the long run.
Conclusion
Setting up your own Git and CI/CD platform with Forgejo Actions is a powerful way to gain full control over your development processes, ensure data sovereignty, and optimize costs in the long term. Its lightweight nature, easy installation, and familiar Actions workflow definition make it a versatile solution suitable for a wide range of organizations, from small teams to larger enterprises.
In this guide, we covered the fundamental steps from Forgejo installation, configuring Actions runners, creating your first workflow, to advanced scenarios and system maintenance. The flexibility and customization options offered by self-hosting allow you to tailor your software development practices to your specific needs. The next step will be to design custom workflows for your projects and unlock the full potential of Forgejo Actions.
Top comments (0)