Author | Nejat Hakan |
nejat.hakan@outlook.de | |
PayPal Me | https://paypal.me/nejathakan |
Git Server Forgejo
Introduction
Welcome to the comprehensive guide on self-hosting your own Git server using Forgejo. In the modern software development landscape, version control systems (VCS) are indispensable tools. Git has emerged as the de facto standard for VCS, enabling developers to track changes, collaborate effectively, and manage codebases of all sizes. While popular hosted services like GitHub, GitLab, and Bitbucket offer convenience, self-hosting your Git server provides unparalleled control, privacy, security, and customization.
Forgejo is a community-driven, lightweight, and easy-to-set-up self-hosted Git service. It originated as a fork of Gitea, aiming for a more community-led governance model while maintaining compatibility and focusing on stability and usability. Think of Forgejo as your personal powerhouse for managing Git repositories, similar in core functionality to GitHub or GitLab, but running entirely on your own infrastructure.
Why Choose Forgejo for Self-Hosting?
- Lightweight: Forgejo is written in Go, resulting in a single binary with minimal resource consumption compared to heavier platforms like GitLab. This makes it ideal for running on modest hardware, including Raspberry Pis or small virtual private servers (VPS).
- Full Control: You own your data. No third-party terms of service changes, unexpected feature deprecations, or pricing model shifts will affect your core operations. You decide the update schedule, the features you enable, and the security policies you enforce.
- Enhanced Privacy: Your code repositories, issues, and project discussions remain on your server, reducing exposure to external data breaches or surveillance.
- Customization: Tailor Forgejo's appearance, integrate it with your existing authentication systems (LDAP, OAuth), and extend its functionality through webhooks or its API.
- Cost-Effective: While hosted platforms often have free tiers, advanced features or larger teams typically require paid subscriptions. Self-hosting Forgejo eliminates these recurring costs, with expenses limited to your server hardware/hosting and maintenance time.
- Community Driven: Forgejo is developed and maintained by an active community, ensuring its continued evolution and responsiveness to user needs, independent of single corporate control.
- Learning Opportunity: Setting up and managing your own Git server is an excellent way to deepen your understanding of Git, web servers, databases, networking, and system administration.
Prerequisites
Before embarking on this journey, we assume you have a foundational understanding of the following concepts and access to the necessary resources:
- Linux Command Line: Familiarity with navigating the shell, editing files (using
nano
,vim
, or similar), managing services, and understanding file permissions. Most examples will use Debian/Ubuntu-based commands (apt
). - Basic Networking: Understanding IP addresses, ports, and DNS.
- Git Fundamentals: Knowledge of basic Git commands like
clone
,add
,commit
,push
,pull
,branch
, andmerge
. - Server Environment: Access to a server (physical machine, virtual machine, or VPS) running a Linux distribution. At least 1 CPU core, 512MB RAM (1GB+ recommended for smoother operation, especially with CI/CD), and sufficient disk space for your repositories and the Forgejo installation.
- (Optional but Recommended) Docker and Docker Compose: While Forgejo can be installed from a binary, using Docker simplifies installation, upgrades, and dependency management significantly. We will heavily feature the Docker approach.
- (Optional) Domain Name: A registered domain name is required if you want to access your Forgejo instance via a memorable name and secure it with HTTPS/SSL certificates.
This guide is structured progressively, starting with basic installation and usage, moving to intermediate configurations like HTTPS and database management, and finally covering advanced topics such as CI/CD, customization, and security hardening. Each section includes theoretical explanations followed by hands-on workshops to solidify your learning. Let's begin!
1. Basic Setup and Usage
This section covers the essentials to get your Forgejo instance up and running, configure it for the first time, and perform fundamental Git operations. We'll primarily use Docker for ease of deployment.
Understanding Forgejo and Self-Hosting Benefits Revisited
Before we dive into the installation, let's reiterate the core concepts. Forgejo acts as a central hub for your Git repositories. It provides a web interface for browsing code, managing issues, tracking pull requests (or merge requests), managing users and organizations, and much more. Unlike simply having Git installed locally, Forgejo provides the server component – the remote location (origin
in Git terms) where collaborators can push and pull changes.
The benefits of self-hosting, as mentioned earlier, revolve around control, privacy, and customization. Imagine your university project group needing a private space to collaborate on code without relying on external services or facing limitations of free tiers. Or consider a personal project where you want absolute ownership over your codebase and its history. Forgejo empowers these scenarios. It's not just about storing code; it's about creating a dedicated, controlled environment for software development collaboration.
Compared to alternatives:
- vs. GitHub/GitLab.com: Forgejo offers privacy and control, no vendor lock-in, but requires you to manage the infrastructure (updates, backups, security).
- vs. Self-Hosted GitLab: Forgejo is significantly more lightweight, requiring fewer server resources. GitLab offers a broader feature set out-of-the-box (advanced CI/CD, security scanning, etc.), often appealing to larger enterprises, but with higher complexity and resource demands.
- vs. Gitea: Forgejo is a fork of Gitea, focused on a community governance model (Codeberg drives much of its development). Functionality is very similar, and migration between them is generally straightforward. Forgejo aims for stability and a transparent development process.
Choosing Forgejo means opting for a balance of features, performance, and community-driven development, ideal for individuals, small teams, educational settings, and organizations prioritizing resource efficiency and control.
Workshop Setting Up Prerequisites
This workshop ensures your server environment is ready for the Forgejo installation using Docker.
Objective: Install Docker and Docker Compose on your Linux server.
Assumptions: You are logged into your Linux server (Ubuntu/Debian assumed) via SSH or direct console access with sudo
privileges.
Steps:
-
Update Package Lists: Ensure your package manager has the latest list of available software.
- Explanation:
apt update
downloads package information from all configured sources. It's crucial to do this before installing new packages to ensure you get the latest versions and dependencies are correctly resolved.sudo
executes the command with administrative privileges.
- Explanation:
-
Install Prerequisite Packages: Docker installation requires a few helper packages.
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
- Explanation:
apt-transport-https
: Allowsapt
to retrieve packages over HTTPS.ca-certificates
: Allows the system to check security certificates.curl
: A tool to transfer data from or to a server (used here to download Docker's GPG key).software-properties-common
: Provides scripts for managing software sources.gnupg
: GNU Privacy Guard, used for handling cryptographic keys (like Docker's repository key).lsb-release
: Provides information about the Linux Standard Base and distribution.-y
: Automatically answers "yes" to prompts during installation.
- Explanation:
-
Add Docker's Official GPG Key: This verifies the authenticity of the Docker packages.
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
- Explanation:
- The first command creates a directory (
/etc/apt/keyrings
) to store GPG keys, ensuring proper permissions (0755
). curl -fsSL ...
downloads the GPG key.-f
fails silently on server errors,-s
runs in silent mode,-S
shows errors if-s
is used,-L
follows redirects.| sudo gpg --dearmor -o ...
pipes the downloaded key to thegpg
command.--dearmor
converts the key from ASCII-armored format to the binary format apt uses.-o
specifies the output file.- The final
chmod
ensures the key file is readable by all users, whichapt
requires.
- The first command creates a directory (
- Explanation:
-
Add the Docker Repository: Configure
apt
to know where to download Docker from.echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
- Explanation:
echo "..."
constructs the repository source line.arch=$(dpkg --print-architecture)
dynamically inserts your server's architecture (e.g.,amd64
,arm64
).signed-by=...
tellsapt
which key to use to verify packages from this repository.$(lsb_release -cs)
dynamically inserts your Ubuntu/Debian version codename (e.g.,jammy
,bullseye
).stable
indicates you want the stable release channel of Docker.| sudo tee ...
writes the output to the specified file (/etc/apt/sources.list.d/docker.list
).tee
is used so it can write the file withsudo
privileges.> /dev/null
suppresses the output oftee
to the standard output.
- Explanation:
-
Update Package Lists Again: Now that the Docker repository is added, update
apt
's knowledge. -
Install Docker Engine, CLI, Containerd, and Docker Compose:
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
- Explanation:
docker-ce
: Docker Community Edition (the engine).docker-ce-cli
: Docker command-line interface.containerd.io
: A container runtime.docker-buildx-plugin
: Enables advanced build features with BuildKit.docker-compose-plugin
: Integratesdocker compose
command directly into the Docker CLI (newer method). Older systems might require installingdocker-compose
separately.
- Explanation:
-
Verify Docker Installation: Check that Docker is running.
- Explanation: This command downloads a minimal test image and runs it in a container. If successful, it prints a confirmation message, indicating Docker is working correctly. You should see output like "Hello from Docker!".
-
(Optional but Recommended) Add Your User to the
docker
Group: This allows you to run Docker commands withoutsudo
.- Explanation:
usermod -aG
adds (-a
) the user ($USER
, which automatically expands to your current username) to the specified group (docker
,-G
). - Important: You need to log out and log back in (or run
newgrp docker
in your current shell) for this change to take effect. After logging back in, test withdocker ps
(withoutsudo
).
- Explanation:
Conclusion: You have successfully installed Docker Engine and Docker Compose on your server. Your environment is now prepared for deploying Forgejo using containers.
Installation with Docker
Docker simplifies the Forgejo installation immensely. We'll use docker-compose
, a tool for defining and running multi-container Docker applications. This allows us to manage Forgejo and its potential database (if not using SQLite) together easily.
Core Concepts:
- Image: A read-only template containing the application (Forgejo) and its dependencies. We'll use the official Forgejo image (
codeberg.org/forgejo/forgejo
). - Container: A runnable instance of an image. It's isolated from the host system and other containers.
- Volume: A mechanism to persist data generated by containers outside the container's lifecycle. This is crucial for storing Forgejo's configuration, repositories, and database data. If the container is removed and recreated, data in volumes remains.
- Port Mapping: Exposes ports from the container to the host machine, allowing you to access the Forgejo web interface and SSH service.
docker-compose.yml
: A YAML file defining the services, networks, and volumes for the application.
Basic docker-compose.yml
for Forgejo (using SQLite):
version: "3.8" # Specify compose file version
networks: # Define a network for services to communicate
forgejo_net:
external: false
services:
forgejo:
image: codeberg.org/forgejo/forgejo:7.0 # Use the desired Forgejo version tag (check releases!)
container_name: forgejo_server
environment:
- USER_UID=1000 # Run Forgejo process as UID 1000 inside the container
- USER_GID=1000 # Run Forgejo process as GID 1000 inside the container
networks: # Connect container to the defined network
- forgejo_net
volumes:
- ./forgejo/data:/data # Map host directory to container's /data for persistent data
- /etc/timezone:/etc/timezone:ro # Sync timezone with host (read-only)
- /etc/localtime:/etc/localtime:ro # Sync localtime with host (read-only)
ports:
- "3000:3000" # Map host port 3000 to container port 3000 (Web UI)
- "2222:22" # Map host port 2222 to container port 22 (SSH) - Avoid host's SSH port 22!
restart: unless-stopped # Automatically restart the container unless manually stopped
depends_on: [] # Add database service name here if using external DB like PostgreSQL
# If you were using persistent Docker volumes instead of bind mounts:
# volumes:
# forgejo_data:
# driver: local
Explanation of the docker-compose.yml
:
version: "3.8"
: Defines the version of the Compose file syntax.networks
: Declares a custom networkforgejo_net
. Containers on the same Docker network can easily communicate with each other by name.external: false
means Docker Compose will create this network if it doesn't exist.services
: Defines the containers to be run.forgejo
: The name of our service.image
: Specifies the Docker image to use.codeberg.org/forgejo/forgejo:7.0
pulls version 7.0. It's good practice to pin to a specific version rather thanlatest
for predictability. Always check the Forgejo releases page for current stable versions.container_name
: Assigns a specific name to the running container for easier identification.environment
: Sets environment variables inside the container.USER_UID
andUSER_GID
are important for file permissions. They ensure that files created by Forgejo inside the volume have the correct ownership on the host machine. You should ideally match these to the user running Docker or a dedicated user on the host.1000:1000
is common for the default user on many Linux systems.networks
: Attaches this service to theforgejo_net
network.volumes
: Defines data persistence../forgejo/data:/data
: This is a bind mount. It maps theforgejo/data
directory relative to where you rundocker-compose up
on your host machine to the/data
directory inside the container. Forgejo stores all its vital data (repositories, configuration, attachments, etc.) within/data
. This is the most critical part for data safety./etc/timezone:/etc/timezone:ro
and/etc/localtime:/etc/localtime:ro
: These map the host's time settings into the container (read-only) ensuring logs and timestamps within Forgejo match the host server time.
ports
: Maps host ports to container ports (HOST:CONTAINER
)."3000:3000"
: Makes the Forgejo web UI (running on port 3000 inside the container) accessible on port 3000 of your host machine."2222:22"
: Makes the Forgejo SSH server (running on port 22 inside the container) accessible on port 2222 of your host machine. We use 2222 on the host to avoid conflicting with the standard SSH daemon typically running on port 22.
restart: unless-stopped
: Policy for restarting the container.unless-stopped
ensures the container restarts automatically if it crashes or if the Docker daemon restarts (e.g., after a server reboot), unless you explicitly stop it usingdocker stop forgejo_server
ordocker-compose stop
.depends_on: []
: If you add a database service (like PostgreSQL) in the samedocker-compose.yml
, you would list its service name here (e.g.,depends_on: [db]
) to ensure Forgejo starts after the database is ready.
Workshop Deploying Forgejo using Docker Compose
Objective: Launch the Forgejo container using the docker-compose.yml
file.
Assumptions: You have Docker and Docker Compose installed (from the previous workshop) and are in a terminal on your server.
Steps:
-
Create a Project Directory: It's good practice to keep your Compose files and related data organized.
- Explanation: Creates a directory named
forgejo-server
and changes the current directory into it. The./forgejo/data
volume path in thedocker-compose.yml
will now refer toforgejo-server/forgejo/data
on your host.
- Explanation: Creates a directory named
-
Create the
docker-compose.yml
File: Use a text editor likenano
to create the file.- Explanation: Opens the
nano
text editor to create/edit thedocker-compose.yml
file.
- Explanation: Opens the
-
Paste the YAML Content: Copy the
docker-compose.yml
content provided in the section above and paste it into thenano
editor.- Verify: Double-check the indentation (YAML is sensitive to spaces, typically 2 spaces per level) and ensure you've chosen a recent, stable Forgejo version tag for the
image
. - Save and Exit: In
nano
, pressCtrl+X
, thenY
to confirm saving, andEnter
to confirm the filename.
- Verify: Double-check the indentation (YAML is sensitive to spaces, typically 2 spaces per level) and ensure you've chosen a recent, stable Forgejo version tag for the
-
Create the Data Directory: The bind mount
./forgejo/data
needs to exist on the host before starting the container, and Docker needs appropriate permissions.mkdir -p forgejo/data # Optional but recommended: Set permissions matching the UID/GID in the compose file # sudo chown 1000:1000 forgejo/data
- Explanation:
mkdir -p
creates the directory and any necessary parent directories (forgejo
in this case). Thechown
command (if you run it) changes the owner and group of theforgejo/data
directory to UID 1000 and GID 1000, matching theUSER_UID
andUSER_GID
environment variables in thedocker-compose.yml
. This helps prevent potential permission issues when Forgejo tries to write data into the volume from within the container. You might needsudo
forchown
.
- Explanation:
-
Start Forgejo: Run Docker Compose in detached mode (
-d
).docker compose up -d # If you are on an older system without the compose plugin, use: # docker-compose up -d
- Explanation:
docker compose up
reads thedocker-compose.yml
file, creates the network (if needed), pulls the Forgejo image (if not already present), creates, and starts the container(s) defined in the file. The-d
flag runs the containers in the background (detached mode) so you get your terminal prompt back.
- Explanation:
-
Check Container Status: Verify that the container is running.
- Explanation:
docker ps
lists currently running containers. You should see a container namedforgejo_server
listed, showing the ports it exposes and its status (e.g.,Up X seconds
).
- Explanation:
-
Check Logs (Optional but useful for troubleshooting):
- Explanation: Displays the logs generated by the Forgejo application inside the container. This is the first place to look if the container doesn't start correctly or if you encounter issues later. Use
docker logs -f forgejo_server
to follow the logs in real-time.
- Explanation: Displays the logs generated by the Forgejo application inside the container. This is the first place to look if the container doesn't start correctly or if you encounter issues later. Use
Conclusion: Forgejo is now running inside a Docker container! It's listening on ports 3000 (HTTP) and 2222 (SSH) on your server's IP address. In the next step, we'll perform the initial web-based configuration.
Initial Configuration Wizard
When you first access the Forgejo web interface after a fresh installation, you'll be greeted by an installation page (/install
). This wizard guides you through the essential configuration settings required for Forgejo to operate. These settings are then saved to the main configuration file, app.ini
, located within the persistent data volume (/data/forgejo/conf/app.ini
inside the container, which corresponds to ./forgejo/data/forgejo/conf/app.ini
on your host).
Key Configuration Options:
-
Database Settings:
- Database Type: Since we used the basic Docker setup without an external database, the default and recommended option here is SQLite3. It's simple, stores the database in a single file within the
/data
volume, and requires no separate database server. For more demanding scenarios (covered in Intermediate), you might choose PostgreSQL or MySQL/MariaDB. - Path: When using SQLite3, this specifies the location of the database file. The default (
data/forgejo.db
) within the container's/data
directory is usually fine.
- Database Type: Since we used the basic Docker setup without an external database, the default and recommended option here is SQLite3. It's simple, stores the database in a single file within the
-
General Settings:
- Site Title: The name displayed in the browser tab and on the site header (e.g., "My University Projects", "Personal Git Server").
- Repository Root Path: The path inside the container where Git repository data will be stored. The default (
/data/git/repositories
) is correct for our Docker setup, as it falls within the mapped/data
volume. Do not change this unless you have manually configured different volume mappings. - Run As Username: The system username Forgejo runs as inside the container. This should match the user configured by the Docker image (often
git
, which corresponds to theUSER_UID
/USER_GID
we set). The default is usually correct. - SSH Server Domain: The domain name that users will use to clone/push repositories via SSH (e.g.,
git.yourdomain.com
). If you don't have a domain yet, you can use your server's IP address for now. - SSH Server Port: The external port number mapped to the container's SSH service. In our
docker-compose.yml
, we mapped host port2222
to container port22
. Therefore, you must set this to2222
. Forgejo needs to know this external port to display correct SSH clone URLs likessh://git@yourserver.com:2222/user/repo.git
. - Forgejo Base URL: This is critically important. It's the full URL users will use to access the web interface, including the protocol (http or https) and port (if not standard 80/443). For our initial setup, this will be
http://<your_server_ip>:3000/
. Ensure this is accurate, as Forgejo uses it to generate links and clone URLs. - HTTP Server Port: The port Forgejo listens on inside the container for web traffic. The default is
3000
, which matches ourdocker-compose.yml
.
-
Optional Settings (Can be configured later):
- Email Service Settings: Configure if you want Forgejo to send notification emails (e.g., for registration, password reset, issue notifications). Requires SMTP server details.
- Server and Third-Party Service Settings: Enable/disable features like federation, Gravatar, etc.
- Administrator Account Settings: Crucial! You must create an initial administrator account here. Choose a strong username and password. This account will have full control over the Forgejo instance.
The app.ini
File:
Once you complete the wizard, Forgejo creates the app.ini
file. You can view or edit it later (requires restarting Forgejo for changes to take effect):
- Inside container:
/data/forgejo/conf/app.ini
- On host:
./forgejo/data/forgejo/conf/app.ini
(relative to yourdocker-compose.yml
)
It's highly recommended to familiarize yourself with the structure and options available in app.ini
by consulting the official Forgejo documentation.
Workshop Completing the Initial Setup
Objective: Access the Forgejo web interface and complete the initial configuration wizard.
Assumptions: Forgejo is running via Docker Compose (from the previous workshop). You know your server's public IP address.
Steps:
-
Access Forgejo in Browser: Open your web browser and navigate to
http://<your_server_ip>:3000
.- Replace
<your_server_ip>
with the actual public IP address of your server. - Troubleshooting: If the page doesn't load:
- Verify the Forgejo container is running (
docker ps
). - Check the Forgejo logs (
docker logs forgejo_server
) for errors. - Ensure your server's firewall allows incoming connections on port 3000 (e.g., using
sudo ufw allow 3000/tcp
if using UFW). - Confirm you are using
http
and nothttps
.
- Verify the Forgejo container is running (
- Replace
-
You should see the "Install" page. Take a moment to review the fields.
-
Configure Database:
- Verify Database Type is set to
SQLite3
. - Leave the Path as the default (
data/forgejo.db
).
- Verify Database Type is set to
-
Configure General Settings:
- Site Title: Enter a descriptive title (e.g., "My Personal Forgejo").
- Repository Root Path: Leave the default
/data/git/repositories
. - Run As Username: Leave the default
git
. - SSH Server Domain: Enter your server's IP address for now (e.g.,
192.0.2.100
). If you have a domain pointing to this IP, you can use that. - SSH Server Port: Change this to
2222
(matching ourdocker-compose.yml
host port). - Forgejo Base URL: Enter
http://<your_server_ip>:3000/
. Make sure this is correct and includes thehttp://
prefix and the:3000
port. Trailing slash is important. - HTTP Server Port: Leave the default
3000
.
-
Configure Administrator Account:
- Scroll down to the "Administrator Account Settings" section (it might be under "Optional Settings").
- Enter a Username (e.g.,
admin
or your preferred username). - Enter a strong Password and confirm it.
- Enter your Email Address. This is important for account recovery and notifications if email is configured later.
-
Review Optional Settings: Glance through other settings like "Email Service Settings" and "Server and Third-Party Service Settings". You can leave these as defaults for now and configure them later if needed. Specifically, ensure "Disable Self-registration" is checked unless you intentionally want anyone to be able to sign up. For a personal server, disabling registration is generally safer.
-
Install Forgejo: Click the "Install Forgejo" button at the bottom.
-
Wait for Installation: Forgejo will save the configuration to
app.ini
, set up the database (the SQLite file), and prepare the instance. This might take a few moments. -
Login: Once complete, you should be redirected to the login page or the main dashboard if automatically logged in. Log in using the administrator username and password you just created.
Conclusion: You have successfully configured your Forgejo instance! You can now access the dashboard, create users, organizations, and repositories. The core setup is complete. Remember the key settings like the Base URL and SSH Port, as they are crucial for accessing your repositories.
Core Git Operations with Forgejo
Now that Forgejo is running and configured, let's use it for its primary purpose: managing Git repositories. This involves interacting with Forgejo both through its web interface and using standard Git commands from your local development machine.
Workflow Overview:
- (Forgejo UI) Create a User (if needed, beyond the admin).
- (Forgejo UI) Create a Repository.
- (Local Machine) Clone the empty repository from Forgejo.
- (Local Machine) Add files, commit changes.
- (Local Machine) Push changes back to the Forgejo server.
- (Forgejo UI) View the pushed code, history, etc.
Key Concepts:
- Repository: A project's collection of files and the entire history of changes. On Forgejo, each project gets its own repository.
- Remote: In Git terminology, a remote is a pointer to another copy of the repository, typically on a server. When you clone from Forgejo, it automatically sets up a remote named
origin
pointing back to your Forgejo instance. - Clone URL: The address used by Git to connect to the remote repository. Forgejo provides HTTPS and SSH clone URLs.
- HTTPS URL: Looks like
http://<your_server_ip>:3000/YourUsername/YourRepoName.git
. Usually requires your Forgejo username and password (or an access token) for authentication on push. Simpler to set up initially. - SSH URL: Looks like
ssh://git@<your_server_ip_or_domain>:2222/YourUsername/YourRepoName.git
. Uses SSH keys for authentication, generally considered more secure and convenient for frequent use (no password typing). Requires setting up SSH keys.
- HTTPS URL: Looks like
Using SSH Keys (Recommended):
To use the SSH clone URL, you need to:
-
Generate an SSH Key Pair: If you don't already have one on your local development machine, use
ssh-keygen
.# On your local machine (not the server) ssh-keygen -t ed25519 -C "your_email@example.com" # Follow prompts: accept default file location (~/.ssh/id_ed25519), set a strong passphrase!
- Explanation:
ssh-keygen
creates a private key (~/.ssh/id_ed25519
) and a public key (~/.ssh/id_ed25519.pub
).-t ed25519
specifies the modern and secure Ed25519 algorithm.-C
adds a comment (usually your email) to the key. Protect your private key and use a passphrase!
- Explanation:
-
Add Public Key to Forgejo:
- Log in to your Forgejo web UI.
- Go to User Settings (click your profile picture in the top right -> Settings).
- Navigate to the "SSH / GPG Keys" tab.
- Click "Add Key".
- Copy the entire content of your public key file (
~/.ssh/id_ed25519.pub
). You can display it usingcat ~/.ssh/id_ed25519.pub
on your local machine. - Paste the public key content into the "Content" text area in Forgejo.
- Give the key a recognizable "Key Name" (e.g., "My Laptop").
- Click "Add Key".
Now, when you perform Git operations using the SSH URL, Git will automatically use your private key to authenticate with Forgejo (which verifies it against the stored public key). If your key has a passphrase, you'll be prompted to enter it.
Workshop Creating Your First Repository and Pushing Code
Objective: Create a repository in Forgejo, clone it locally, make a change, and push it back using Git over SSH.
Assumptions:
- Forgejo is running and configured.
- You are logged into the Forgejo web UI as the administrator (or another user).
- You have Git installed on your local development machine.
- You have generated an SSH key pair locally and added the public key to your Forgejo user settings (as described above).
- You know your server's IP address or domain name.
Steps:
-
Create a New Repository in Forgejo:
- In the Forgejo web UI, click the "+" icon in the top right corner and select "New Repository".
- Owner: Choose your username.
- Repository Name: Enter
my-first-project
. - Description: (Optional) Add a short description, e.g., "My first repository on self-hosted Forgejo".
- Visibility: Keep it "Private" for now unless you want it publicly accessible.
- Initialize Repository: Check the box for "Initialize Repository with..." and select options like "Add .gitignore" (choose a relevant template, e.g., "Python" or leave as "None") and "Add License" (e.g., "MIT License"). This creates the repo with some initial files, which is common practice.
- Click "Create Repository".
-
Get the SSH Clone URL:
- On the repository page in Forgejo, click the green "Code" button (or similar, might just show the URLs).
- Make sure "SSH" is selected.
- Copy the SSH URL provided. It should look like:
ssh://git@<your_server_ip_or_domain>:2222/YourUsername/my-first-project.git
(replace placeholders with your actual values, including port2222
).
-
Clone the Repository Locally:
- Open a terminal or command prompt on your local development machine.
- Navigate to the directory where you want to store your projects.
- Run the
git clone
command with the copied SSH URL:- (First time connection): You might see a message like "The authenticity of host '[
]:2222' can't be established... Are you sure you want to continue connecting (yes/no/[fingerprint])?". Type yes
and press Enter. This adds the server's host key to your~/.ssh/known_hosts
file. - (SSH Key Passphrase): If your SSH key is protected by a passphrase, you will be prompted to enter it.
- (First time connection): You might see a message like "The authenticity of host '[
- A new directory named
my-first-project
should be created, containing the files you initialized the repository with (.gitignore, LICENSE).
-
Make Changes:
- Navigate into the new repository directory:
- Create a new file:
- Explanation: Creates a file named
README.md
with the text "Hello Forgejo!".
- Explanation: Creates a file named
-
Stage and Commit Changes:
- Check the status:
- Explanation: Shows untracked files (
README.md
).
- Explanation: Shows untracked files (
- Stage the new file:
- Explanation: Tells Git to track changes in
README.md
for the next commit.
- Explanation: Tells Git to track changes in
- Commit the changes:
- Explanation: Records the staged changes as a snapshot in the project's history. The
-m
flag provides a descriptive commit message.
- Explanation: Records the staged changes as a snapshot in the project's history. The
- Check the status:
-
Push Changes to Forgejo:
- Push the committed changes from your local
main
(ormaster
) branch to theorigin
remote (your Forgejo server):- (SSH Key Passphrase): You might be prompted for your SSH key passphrase again.
- You should see output indicating the push was successful, transferring objects to the remote server.
- Push the committed changes from your local
-
Verify in Forgejo Web UI:
- Go back to your browser and refresh the
my-first-project
repository page on your Forgejo instance. - You should now see the
README.md
file listed, and the latest commit message ("Add initial README file") should be visible. You can click on "Commits" to see the history.
- Go back to your browser and refresh the
Conclusion: You have successfully completed the fundamental Git workflow with your self-hosted Forgejo server: creating a repository, cloning it, making local changes, and pushing them back to the server using SSH authentication. You can now repeat steps 4-6 whenever you make further changes to your project.
2. Intermediate Configuration and Management
With the basics covered, this section delves into more advanced configurations to make your Forgejo instance more robust, secure, and production-ready. We'll cover setting up HTTPS, using a more powerful database, managing users effectively, and implementing backup strategies.
Reverse Proxy and HTTPS Setup
Running Forgejo directly exposed on port 3000 via HTTP is acceptable for initial testing or strictly internal networks, but highly discouraged for any instance accessible over the internet. Exposing it directly has several drawbacks:
- No Encryption: All traffic, including login credentials and code, is sent in plain text (HTTP), making it vulnerable to eavesdropping.
- Non-Standard Port: Using port 3000 requires users to remember and type the port number in the URL. Standard web traffic uses port 80 (HTTP) and 443 (HTTPS).
- Limited Features: Directly exposing Forgejo prevents easy implementation of features like caching, rate limiting, or serving multiple websites from the same IP address.
A reverse proxy solves these problems. It's a server (like Nginx or Apache) that sits in front of Forgejo, receives incoming web requests, and forwards them to the Forgejo application running internally.
Benefits of using a Reverse Proxy (e.g., Nginx):
- HTTPS/SSL Termination: The reverse proxy handles the SSL/TLS encryption and decryption. Users connect to the proxy via HTTPS (port 443), and the proxy communicates with Forgejo internally (e.g., over HTTP on port 3000). This secures the connection from the user to your server.
- Standard Ports: Users can access Forgejo via standard ports (e.g.,
https://git.yourdomain.com
), without needing:3000
. - Load Balancing: While less relevant for a single Forgejo instance, reverse proxies can distribute traffic across multiple backend application servers for high availability and performance.
- Caching: Can cache static assets (CSS, JS, images) to speed up loading times and reduce load on Forgejo.
- Security: Can implement additional security measures like rate limiting, blocking malicious IPs, or adding security headers.
- Hosting Multiple Sites: Allows you to run Forgejo alongside other web applications on the same server, using the same IP address but different domain names (virtual hosts).
Using Nginx as a Reverse Proxy:
Nginx is a popular, high-performance web server and reverse proxy. Here's a typical configuration snippet for proxying requests to our Dockerized Forgejo instance:
# /etc/nginx/sites-available/forgejo.conf
server {
listen 80; # Listen on port 80 for incoming HTTP requests
server_name git.yourdomain.com; # Replace with your actual domain name
# Redirect all HTTP traffic to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2; # Listen on port 443 for HTTPS, enable HTTP/2
server_name git.yourdomain.com; # Replace with your actual domain name
# SSL Certificate configuration (using Let's Encrypt / Certbot)
ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem; # Path to your certificate
ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem; # Path to your private key
include /etc/letsencrypt/options-ssl-nginx.conf; # Recommended SSL settings from Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # DH parameters from Certbot
# Security Headers (Optional but recommended)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
add_header X-XSS-Protection "1; mode=block" always;
# add_header Content-Security-Policy "default-src 'self'; ..." # More complex, requires tuning
# Increase max body size for large pushes/uploads
client_max_body_size 100m; # Adjust as needed (e.g., 500m for larger LFS files)
location / {
proxy_pass http://127.0.0.1:3000; # Forward requests to Forgejo running on localhost:3000
# Required proxy headers for Forgejo
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;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# WebSocket support (needed for live updates in UI)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Optional: Improve static file serving (if assets are outside /data)
# location ~ ^/(css|js|img|fonts)/ {
# root /path/to/forgejo/public; # Adjust if needed
# expires 1h;
# }
}
Explanation:
- HTTP Server Block (port 80): Catches all incoming HTTP requests for
git.yourdomain.com
and issues a permanent redirect (301) to the HTTPS version. - HTTPS Server Block (port 443):
listen 443 ssl http2;
: Listens for HTTPS traffic, enables SSL/TLS, and enables HTTP/2 for better performance.server_name
: Specifies the domain this block applies to.ssl_certificate
,ssl_certificate_key
,include
,ssl_dhparam
: These lines configure SSL using certificates obtained from Let's Encrypt via Certbot (we'll cover this in the workshop).add_header ...
: Adds various security headers to enhance protection against common web vulnerabilities (HSTS, clickjacking, XSS).client_max_body_size
: Sets the maximum size for client request bodies, important for allowing large Git pushes or file uploads. Default is often 1MB, which is too small.location / { ... }
: Defines how to handle requests for the root and any sub-paths.proxy_pass http://127.0.0.1:3000;
: The core directive. It forwards the request tohttp://127.0.0.1:3000
, which is where our Dockerized Forgejo instance is listening (we mapped host port 3000 to container port 3000).proxy_set_header ...
: These headers pass crucial information from the original client request to Forgejo (like the original host domain, client IP, and protocol used - HTTPS), allowing Forgejo to generate correct URLs and log accurate information. These are essential for Forgejo to function correctly behind a reverse proxy.- WebSocket headers (
Upgrade
,Connection
): Necessary for real-time features in the Forgejo UI.
Let's Encrypt and Certbot:
Let's Encrypt is a free, automated, and open Certificate Authority (CA). Certbot is a client tool that simplifies obtaining and renewing Let's Encrypt SSL certificates. When configured with the Nginx plugin, Certbot can automatically obtain certificates, configure Nginx to use them, and set up automatic renewal.
Forgejo Configuration Changes:
After setting up the reverse proxy for HTTPS, you need to update Forgejo's configuration (app.ini
) to reflect the new public URL:
[server]
section:ROOT_URL = https://git.yourdomain.com/
(Update to your HTTPS domain)HTTP_PORT = 3000
(This remains the internal port Forgejo listens on)PROTOCOL = http
(Because Nginx talks to Forgejo over plain HTTP internally. Nginx handles the external HTTPS).SSH_DOMAIN = git.yourdomain.com
(Update domain if necessary)SSH_PORT = 2222
(Remains the external SSH port)
You'll need to restart the Forgejo container after editing app.ini
.
Workshop Securing Forgejo with Nginx and Let's Encrypt
Objective: Configure Nginx as a reverse proxy for Forgejo, obtain a Let's Encrypt SSL certificate, and update Forgejo's configuration.
Assumptions:
- Forgejo is running via Docker Compose on ports 3000 (HTTP) and 2222 (SSH).
- You have a registered domain name (e.g.,
git.yourdomain.com
) with DNS properly configured: anA
record pointing your domain to your server's public IP address. - Ports 80 and 443 are open on your server's firewall (
sudo ufw allow 80/tcp
,sudo ufw allow 443/tcp
). Port 3000 might no longer need to be open externally once Nginx is set up, but internal access from Nginx must work. - You are logged into your server with
sudo
privileges.
Steps:
-
Install Nginx:
- Explanation: Installs the Nginx web server.
-
Install Certbot and Nginx Plugin:
- Explanation: Installs Certbot and its plugin for automatically configuring Nginx.
-
Create Nginx Configuration File for Forgejo:
- Use
nano
or another editor to create the Nginx configuration file: - Paste the Nginx configuration example provided in the theory section above into this file.
- Crucially: Replace all instances of
git.yourdomain.com
with your actual domain name. Adjustclient_max_body_size
if needed. - Note: Initially, the SSL configuration lines (
ssl_certificate
,ssl_certificate_key
, etc.) won't work because the certificates don't exist yet. We'll let Certbot handle this. You can leave them commented out for now or let Certbot add/modify them. For simplicity here, we'll assume you paste the full config, and Certbot will adjust it. - Save and close the file (
Ctrl+X
,Y
,Enter
innano
).
- Use
-
Enable the Nginx Site Configuration:
sudo ln -s /etc/nginx/sites-available/forgejo.conf /etc/nginx/sites-enabled/ # Remove the default Nginx welcome page config if it exists sudo rm /etc/nginx/sites-enabled/default
- Explanation: Creates a symbolic link from
sites-available
(where configs are stored) tosites-enabled
(where Nginx looks for active configs). Removing thedefault
config prevents conflicts.
- Explanation: Creates a symbolic link from
-
Test Nginx Configuration:
- Explanation: Checks the Nginx configuration files for syntax errors. If you pasted the SSL lines already, this might initially fail because the certificate files don't exist yet - this is expected at this stage if you included those lines. If it reports other syntax errors, fix them in
forgejo.conf
before proceeding.
- Explanation: Checks the Nginx configuration files for syntax errors. If you pasted the SSL lines already, this might initially fail because the certificate files don't exist yet - this is expected at this stage if you included those lines. If it reports other syntax errors, fix them in
-
Obtain SSL Certificate using Certbot:
- Explanation:
certbot
: Runs the Certbot client.--nginx
: Specifies using the Nginx plugin to automate configuration.-d git.yourdomain.com
: Specifies the domain name(s) to obtain certificates for. Replace with your domain.
- Follow Prompts:
- Enter your email address (for renewal notices).
- Agree to the Terms of Service.
- Choose whether to share your email (optional).
- Certbot will detect the domain in your Nginx config. It will ask if you want it to automatically configure Nginx for HTTPS, including setting up the redirect from HTTP to HTTPS. Choose the option to redirect (usually option 2).
- If successful, Certbot will obtain the certificate, place it in
/etc/letsencrypt/live/git.yourdomain.com/
, modify your/etc/nginx/sites-available/forgejo.conf
to include the necessary SSL directives (or uncomment/correct them if you pasted them earlier), and reload Nginx.
- Explanation:
-
Verify Nginx Reloaded: Check Nginx status.
- It should show as active (running). If Certbot didn't reload it, run
sudo systemctl reload nginx
.
- It should show as active (running). If Certbot didn't reload it, run
-
Update Forgejo Configuration (
app.ini
):- Navigate to your Forgejo project directory on the host (where
docker-compose.yml
is). - Edit the
app.ini
file: - Find the
[server]
section and make these changes (adjustinggit.yourdomain.com
):[server] PROTOCOL = http # Nginx handles external HTTPS DOMAIN = git.yourdomain.com ROOT_URL = https://git.yourdomain.com/ HTTP_ADDR = 0.0.0.0 # Listen on all interfaces inside container HTTP_PORT = 3000 ; ... other settings ... SSH_DOMAIN = git.yourdomain.com # Update if using domain for SSH SSH_PORT = 2222 # External SSH port remains the same ; ... LFS settings if applicable ...
- Save and close the file.
- Navigate to your Forgejo project directory on the host (where
-
Restart Forgejo Container: Apply the configuration changes.
-
Test Access:
- Open your browser and navigate to
https://git.yourdomain.com
(using HTTPS and your domain). - You should be automatically redirected from HTTP if you type
http://...
. - The Forgejo login page should load securely (look for the padlock icon in the browser's address bar).
- Log in and verify that repository clone URLs shown in the UI are now using the correct HTTPS and SSH domain names (e.g.,
https://git.yourdomain.com/User/repo.git
andssh://git@git.yourdomain.com:2222/User/repo.git
). - Try cloning/pushing using the new HTTPS URL (it will likely prompt for your Forgejo username/password) and the updated SSH URL.
- Open your browser and navigate to
Conclusion: You have successfully placed Nginx as a reverse proxy in front of Forgejo, secured it with a free Let's Encrypt SSL certificate, and configured both Nginx and Forgejo to work together seamlessly. Your Forgejo instance is now accessible via a standard, secure HTTPS URL. Certbot will typically handle automatic renewal of the certificate. You can test renewal with sudo certbot renew --dry-run
.
Database Configuration Deep Dive
While SQLite is convenient for single-user or very small instances, it has limitations, particularly concerning concurrent write operations. As your Forgejo usage grows (more users, more frequent pushes, CI/CD activity), performance can degrade because SQLite locks the entire database file during writes.
For better scalability, performance, and robustness, using a dedicated relational database server like PostgreSQL or MySQL/MariaDB is highly recommended. PostgreSQL is often favored in the Forgejo/Gitea community for its reliability and feature set.
Why Use PostgreSQL?
- Concurrency: Handles many simultaneous read and write operations efficiently using row-level locking, significantly improving performance under load compared to SQLite's database-level locking.
- Scalability: Designed to handle large datasets and high transaction volumes gracefully.
- Robustness: Offers features like Point-in-Time Recovery (PITR), replication, and advanced backup options.
- Data Integrity: Enforces stricter data type checking and constraints.
Setting up PostgreSQL for Forgejo:
-
Install PostgreSQL: Install the PostgreSQL server package on your host machine (or run it as a separate Docker container).
-
Create Database and User: Access the PostgreSQL command-line interface and create a dedicated database and user for Forgejo.
- Inside the
psql
shell:-- Create a dedicated database for Forgejo CREATE DATABASE forgejo WITH ENCODING 'UTF8' LC_COLLATE='en_US.UTF-8' LC_CTYPE='en_US.UTF-8' TEMPLATE=template0; -- Create a dedicated user for Forgejo CREATE USER forgejo_user WITH PASSWORD 'YOUR_STRONG_PASSWORD'; -- Replace with a secure password! -- Grant all privileges on the forgejo database to the forgejo_user GRANT ALL PRIVILEGES ON DATABASE forgejo TO forgejo_user; -- Exit psql \q
- Explanation:
sudo -u postgres psql
: Connects to thepsql
shell as the defaultpostgres
superuser.CREATE DATABASE forgejo ...
: Creates a database namedforgejo
. Specifying UTF8 encoding and locale settings ensures proper handling of various character sets.TEMPLATE=template0
is often recommended for clean encoding setup.CREATE USER forgejo_user ...
: Creates a database user namedforgejo_user
. Choose a very strong password.GRANT ALL PRIVILEGES ...
: Gives theforgejo_user
full permissions only on theforgejo
database. This follows the principle of least privilege.
- Inside the
-
Configure Forgejo (
app.ini
): Modify the[database]
section in yourforgejo/data/forgejo/conf/app.ini
file. Stop Forgejo before editing.[database] DB_TYPE = postgres HOST = <database_host>:<database_port> # e.g., 127.0.0.1:5432 or docker_service_name:5432 NAME = forgejo USER = forgejo_user PASSWD = 'YOUR_STRONG_PASSWORD' # Use the password you set! Enclose in single quotes if it contains special characters. SSL_MODE = disable # Change to 'require' or 'verify-full' if connecting over network with SSL LOG_SQL = false # Set to true for debugging SQL queries (very verbose) CONNECT_STR = # Generally leave empty unless needed for specific connection params PATH = data/forgejo.db # This setting is ignored when DB_TYPE is not sqlite3
- Explanation:
DB_TYPE
: Set topostgres
.HOST
: The address and port of the PostgreSQL server.- If PostgreSQL is running on the same host as the Docker daemon (but outside Docker),
127.0.0.1:5432
(default PostgreSQL port) is usually correct if Forgejo can reach the host network. - If PostgreSQL is running as another Docker container on the same Docker network (
forgejo_net
), use the service name defined indocker-compose.yml
(e.g.,db:5432
).
- If PostgreSQL is running on the same host as the Docker daemon (but outside Docker),
NAME
: The database name (forgejo
).USER
: The database user (forgejo_user
).PASSWD
: The password you created forforgejo_user
. Protect this! Consider using Docker secrets for better security in production.SSL_MODE
: Controls SSL connection to the database.disable
is fine for connections onlocalhost
or within a trusted Docker network. Userequire
if connecting over an untrusted network.
- Explanation:
-
Restart Forgejo: After saving
app.ini
, restart the Forgejo container. It will attempt to connect to the PostgreSQL database. Check the logs (docker logs forgejo_server
) to confirm a successful connection and potentially see database migration messages as Forgejo sets up its tables.
Running PostgreSQL in Docker:
Alternatively, you can manage PostgreSQL within your docker-compose.yml
file. This keeps your entire Forgejo stack containerized.
# Example additions to docker-compose.yml
version: "3.8"
networks:
forgejo_net:
external: false
services:
forgejo:
image: codeberg.org/forgejo/forgejo:7.0
container_name: forgejo_server
environment:
- USER_UID=1000
- USER_GID=1000
networks:
- forgejo_net
volumes:
- ./forgejo/data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
# Only expose Nginx ports now, not 3000 or 2222 directly (if using Nginx)
# - "3000:3000" # Keep if needed for direct access or Nginx on different host
- "2222:22" # Keep SSH port mapping
restart: unless-stopped
depends_on: # Add dependency on the database service
- db
db: # Define the PostgreSQL service
image: postgres:15 # Use a specific PostgreSQL version
container_name: forgejo_postgres
restart: unless-stopped
environment:
- POSTGRES_USER=forgejo_user # Set desired username
- POSTGRES_PASSWORD=YOUR_STRONG_PASSWORD # Set desired password (use Docker secrets in prod!)
- POSTGRES_DB=forgejo # Set desired database name
networks:
- forgejo_net
volumes:
- ./postgres/data:/var/lib/postgresql/data # Persist PostgreSQL data
volumes: # Define persistent Docker volume (alternative to bind mount) - less used here as bind mounts specified
# postgres_data: # Example if using named volume instead of bind mount for PG
# driver: local
Explanation of db
service:
image: postgres:15
: Uses the official PostgreSQL image, version 15.container_name
: Gives the container a specific name.restart: unless-stopped
: Ensures the database restarts automatically.environment
: Configures the PostgreSQL container on first run. It automatically creates the specified user, password, and database. Use strong passwords!networks
: Connects the database container to the sameforgejo_net
network as Forgejo.volumes
: Mounts./postgres/data
on the host to/var/lib/postgresql/data
inside the container, ensuring the database data persists across container restarts.
If using this Docker Compose setup for PostgreSQL, the HOST
in Forgejo's app.ini
[database]
section should be db:5432
(service name and default port). The forgejo
service also now includes depends_on: [db]
to ensure the database container starts before Forgejo attempts to connect.
Data Migration:
If you initially set up Forgejo with SQLite and want to switch to PostgreSQL, you will need to migrate your data. Forgejo includes a command-line tool for dumping data (forgejo dump
) and potentially importing it, but migrating between database types can sometimes be complex. Often, the easiest path (if feasible) is to:
- Back up your repositories (the
git-repositories
directory). - Set up the new PostgreSQL database.
- Configure Forgejo to use PostgreSQL (pointing to an empty database).
- Restart Forgejo; it will initialize the schema in PostgreSQL.
- Re-create users, organizations, etc.
- Use Forgejo's migration features (or manual pushes) to re-import your repositories from the backups or another source.
Consult the official Forgejo documentation for the most current recommendations on database migration.
Workshop Configuring Forgejo with PostgreSQL
Objective: Reconfigure a running Forgejo instance (or set up a new one) to use PostgreSQL running in a separate Docker container managed by Docker Compose.
Assumptions:
- You are comfortable editing
docker-compose.yml
andapp.ini
. - You have Docker and Docker Compose installed.
- You are in the
forgejo-server
directory created earlier. - Warning: This workshop assumes you are either starting fresh or willing to potentially lose existing Forgejo data (users, issues, non-repo data) if migrating from SQLite without a proper data migration procedure. We focus here on the connection setup. If preserving data is critical, research Forgejo's data dump/restore/migration tools first.
Steps:
-
Stop Forgejo: If Forgejo is currently running, stop it.
- Explanation:
down
stops and removes the containers defined in thedocker-compose.yml
, but leaves the volumes (like./forgejo/data
) intact.
- Explanation:
-
Modify
docker-compose.yml
:- Edit the
docker-compose.yml
file: - Add the
db
service definition for PostgreSQL and thedepends_on
section to theforgejo
service, as shown in the "Running PostgreSQL in Docker" example above. - Choose a strong password for
POSTGRES_PASSWORD
and remember it. - (Optional) If using Nginx, you might remove the
"3000:3000"
port mapping from theforgejo
service, as access should primarily be through Nginx. Keep the SSH port mapping ("2222:22"
). - Your complete
docker-compose.yml
should look similar to the example provided in the theory section, incorporating bothforgejo
anddb
services. - Save and close the file.
- Edit the
-
Create PostgreSQL Data Directory: Create the host directory for the PostgreSQL volume.
-
Modify Forgejo
app.ini
for PostgreSQL:- Edit the
app.ini
file: - Locate the
[database]
section. - Modify the settings as follows (using the user/pass/db defined in
docker-compose.yml
):[database] DB_TYPE = postgres HOST = db:5432 # Use the service name 'db' and default PG port NAME = forgejo USER = forgejo_user PASSWD = 'YOUR_STRONG_PASSWORD' # Use the SAME password as in docker-compose.yml! SSL_MODE = disable ; Remove or comment out the PATH line for SQLite ; PATH = data/forgejo.db LOG_SQL = false
- Ensure
DB_TYPE
ispostgres
andHOST
points to the service namedb
. - Save and close the file.
- Edit the
-
Start the Stack: Launch both Forgejo and PostgreSQL using Docker Compose.
- Explanation: Docker Compose will create the
forgejo_net
network, start thedb
container (PostgreSQL), wait for it to initialize (it will create the user/db specified in environment variables), and then start theforgejo
container. Thedepends_on
ensures this order.
- Explanation: Docker Compose will create the
-
Check Logs: Monitor the logs of both containers to ensure they start correctly and Forgejo connects to the database.
docker compose logs -f forgejo # Press Ctrl+C to stop following forgejo logs docker compose logs -f db # Press Ctrl+C to stop following db logs
- In the
forgejo
logs, look for messages indicating a successful connection to the PostgreSQL database and potentially messages about running database migrations (e.g., "Initializing ORM engine" followed by "Database schema migration finished"). - In the
db
(PostgreSQL) logs, look for messages like "database system is ready to accept connections".
- In the
-
Test Forgejo: Access your Forgejo instance via its web URL (e.g.,
https://git.yourdomain.com
).- If this is a fresh setup after switching databases, you will likely be presented with the
/install
page again, unless a previousapp.ini
already exists. If you see the install page, fill it out again, ensuring the database section correctly reflects the PostgreSQL settings you just configured inapp.ini
. Forgejo should detect the existingapp.ini
and use those values, skipping the install page if the DB connection works. - Log in (you might need to recreate the admin user if starting fresh).
- Try creating a repository or performing other actions to confirm functionality.
- If this is a fresh setup after switching databases, you will likely be presented with the
Conclusion: You have successfully configured your Forgejo stack to use PostgreSQL running as a separate Docker container. This setup provides better performance and scalability compared to SQLite. Remember to manage the PostgreSQL password securely and include the PostgreSQL data volume (./postgres/data
) in your backup strategy.
User Management and Permissions
Forgejo provides flexible mechanisms for managing users, organizing them into groups (Organizations and Teams), and controlling access to repositories.
User Accounts:
- Registration: Can be open (anyone can register), restricted (requires admin approval or email domain whitelist), or disabled (only admins can create users). Configured in
app.ini
([service]
section,DISABLE_REGISTRATION
,ENABLE_CAPTCHA
,REGISTER_EMAIL_CONFIRM
,ALLOWED_EMAIL_DOMAINS
) or via the Admin Panel (Site Administration -> Configuration -> Application Settings). For private instances, disabling registration is recommended. - Authentication: Supports local database authentication, LDAP, OAuth2 (e.g., GitHub, Google), SMTP, and PAM. Configurable via the Admin Panel (Site Administration -> Authentication Sources).
- User Roles:
- Regular User: Standard permissions. Can create repositories (personal or in orgs/teams they belong to), push/pull based on permissions.
- Administrator: Full control over the instance via the Admin Panel (manage users, repositories, system settings, authentication sources).
- Restricted: Limited permissions, cannot create repositories, organizations, etc. Useful for users who only need read access or issue tracking capabilities.
- Admin Panel: Accessible only to administrators (usually via
/admin
). Provides tools to:- Create, edit, delete users.
- Manage user permissions (promote to admin, restrict).
- View system status, queues, cron tasks.
- Modify instance configuration (
app.ini
settings via UI). - Manage organizations and repositories globally.
Organizations:
- Act as containers for repositories and teams, typically representing a company, project group, or department.
- Users can be members of multiple organizations.
- Each organization has owners and members. Owners have full administrative control within the organization.
- Repositories can belong to an organization instead of an individual user.
Teams:
- Exist within organizations.
- Group users together (e.g., "Developers", "Testers", "Documentation").
- Permissions are assigned to teams per repository. This is the primary way to manage fine-grained access control within an organization.
- Team Permissions Levels:
- Read: Can view repository code, issues, wiki, releases. Can clone/pull.
- Write: Read permissions + Can push code to branches (respecting branch protection rules), manage issues/labels/milestones, edit wiki, manage releases.
- Admin: Write permissions + Can manage repository settings, collaborators, webhooks, deploy keys, and team access to the repository.
- Team Types:
- Owner Team: Automatically created (usually called "Owners"), has Admin permissions on all organization repositories. Organization owners are typically members.
- Regular Teams: Created manually for specific permission groupings.
Repository Access Control:
Access to a repository is determined by a combination of factors:
- Repository Visibility: Public (accessible to anyone, including non-logged-in users) or Private (requires explicit permissions).
- User Ownership: The user who owns the repository has Admin access.
- Organization Ownership: If owned by an organization:
- Organization Owners have Admin access (via the Owners team).
- Teams the user belongs to might grant Read, Write, or Admin access.
- Collaboration: Individual users can be added directly as collaborators to a repository with specific permissions (Read, Write, Admin), bypassing the need for teams in simpler scenarios.
Best Practices:
- For collaborative projects, create an Organization.
- Create Teams within the organization based on roles or responsibilities.
- Grant permissions to Teams on specific repositories rather than adding many individual collaborators. This simplifies management as users join or leave roles/teams.
- Regularly review user accounts and permissions, especially administrator access.
- Use strong passwords and encourage users to enable Two-Factor Authentication (2FA) if available/needed.
Workshop Setting Up an Organization and Team Permissions
Objective: Create an organization, invite users (or create new ones), create teams, and assign specific permissions to a repository within that organization.
Assumptions:
- Forgejo is running and accessible.
- You are logged in as an administrator.
Steps:
-
Create New Users (if needed):
- Navigate to the Admin Panel (Top right profile -> Site Administration).
- Go to User Management -> User Accounts.
- Click "Create User".
- Fill in the details for at least two new users (e.g.,
dev1
,dev2
). Provide temporary passwords (users can change them later) and email addresses. Ensure "Activated" is checked. Leave "Administrator" unchecked. - Click "Create User" for each new user.
-
Create an Organization:
- Navigate back to the main dashboard (click the Forgejo logo).
- Click the "+" icon in the top right corner and select "New Organization".
- Organization Name: Enter
university-project-x
. - (Optional) Fill in description, location, website.
- Click "Create Organization".
- You will be taken to the organization's page. Your admin user is automatically an owner.
-
Add Members to the Organization (Optional but good practice):
- On the organization page (
university-project-x
), go to the "Members" tab. - Start typing the username of one of the users you created (e.g.,
dev1
) in the "Invite or add user" box. Select the user. - Click "Add Member". Repeat for
dev2
. - By default, they are added as "Members". You can change roles later if needed (e.g., make someone an Owner).
- On the organization page (
-
Create Teams:
- On the organization page, go to the "Teams" tab.
- You'll see the default "Owners" team.
- Click "New Team".
- Team Name:
developers
. - Description: (Optional) "Core development team".
- Permissions: Choose
Write
(This sets the default permission level this team gets when added to new repos, but we'll override it per-repo). - Team Units: Select the sections the team should have access to (Code, Issues, Pull Requests, etc.). Defaults are usually fine.
- Click "Create Team".
- Click "New Team" again.
- Team Name:
reviewers
. - Description: (Optional) "Code reviewers".
- Permissions: Choose
Read
. - Click "Create Team".
-
Add Members to Teams:
- Go back to the "Teams" tab.
- Click on the
developers
team name. - In the "Add member" box, type
dev1
and add them. - Go back to the "Teams" tab.
- Click on the
reviewers
team name. - In the "Add member" box, type
dev2
and add them.
-
Create a Repository within the Organization:
- Navigate back to the
university-project-x
organization page. - Click the "New Repository" button.
- Owner: Ensure
university-project-x
is selected. - Repository Name:
project-backend
. - Visibility: Set to
Private
. - Initialize it with a README or leave it empty.
- Click "Create Repository".
- Navigate back to the
-
Assign Team Permissions to the Repository:
- On the
project-backend
repository page, go to "Settings". - Navigate to the "Collaborators & Teams" tab (or similar name, might be just "Collaborators").
- Scroll down to the "Teams" section.
- In the "Add Team" dropdown, select the
developers
team. ChooseWrite
permission from the adjacent dropdown. Click "Add Team". - In the "Add Team" dropdown, select the
reviewers
team. ChooseRead
permission. Click "Add Team".
- On the
-
Verify Permissions (Simulate User Login):
- Log out from your administrator account.
- Log in as
dev1
(the developer).- Navigate to the
university-project-x
organization. - Access the
project-backend
repository. - Verify
dev1
can see the repository content. - Verify
dev1
can see the clone URLs (HTTPS/SSH). They should be able to clone and push (Write access). - Check repository "Settings" -
dev1
should not have access to most settings, only those allowed by Write permissions.
- Navigate to the
- Log out from
dev1
. - Log in as
dev2
(the reviewer).- Navigate to the
university-project-x
organization. - Access the
project-backend
repository. - Verify
dev2
can see the repository content and clone URLs. - Try simulating a push (or look for UI elements) -
dev2
should not be able to push code (Read access only). They should be able to view code, issues, etc. - Check repository "Settings" -
dev2
should have very limited or no access.
- Navigate to the
Conclusion: You have successfully structured collaboration using an organization and teams. The developers
team has Write access to the project-backend
repository, allowing them to contribute code, while the reviewers
team has Read access, suitable for code review or viewing progress. This demonstrates a scalable way to manage permissions for multiple users and projects.
Backup and Restore Strategies
Your Forgejo instance contains critical data: Git repositories, database information (users, issues, permissions, metadata), configuration files, and potentially attachments or LFS objects. Losing this data can be catastrophic. Implementing a robust backup and restore strategy is non-negotiable for any self-hosted service.
What Needs Backing Up?
Assuming our Docker setup with bind mounts (./forgejo/data
and potentially ./postgres/data
):
- Forgejo Configuration: The
app.ini
file (./forgejo/data/forgejo/conf/app.ini
). - Forgejo Data Directory: The entire
/data
volume mapped to./forgejo/data
on the host. This includes:- Git Repositories: Located in
/data/git/repositories
(host:./forgejo/data/git/repositories
). This is often the largest part. - Database (if SQLite): The
forgejo.db
file (e.g.,./forgejo/data/data/forgejo.db
if default path used). - Attachments: Files uploaded to issues/releases (
./forgejo/data/data/attachments
). - LFS Objects: If using Large File Storage (
./forgejo/data/lfs
). - Avatars, Indices, Logs: Other miscellaneous but potentially useful data.
- Git Repositories: Located in
- Database (if PostgreSQL/MySQL): A separate dump of the database. Simply copying the data files (
./postgres/data
) while the database is running is not reliable and can lead to corrupted backups. You need to use the database's specific dump tool (e.g.,pg_dump
for PostgreSQL). - (Optional but Recommended) Docker Compose File: Your
docker-compose.yml
file, which defines how your services are configured. - (Optional but Recommended) Reverse Proxy Configuration: Your Nginx/Apache configuration files.
Backup Strategy:
The key challenge is ensuring consistency, especially between the database and the repository files. A Git push, for example, updates both the repository files on disk and related metadata in the database. Backing them up at slightly different times could lead to inconsistencies.
Recommended Approach (Offline Backup):
This is the safest method to guarantee consistency.
- Stop Forgejo: Prevent any new writes to the repositories or database.
- Backup Database:
- PostgreSQL (running host or separate container): Use
pg_dump
.# If PG running on host: sudo -u postgres pg_dump -U forgejo_user -Fc forgejo > forgejo_backup_$(date +%Y%m%d_%H%M%S).pgdump # If PG running in Docker container named 'forgejo_postgres': docker exec forgejo_postgres pg_dump -U forgejo_user -Fc forgejo > forgejo_backup_$(date +%Y%m%d_%H%M%S).pgdump
-U forgejo_user
: Specifies the database user.-Fc
: Specifies the custom compressed dump format (good for consistency and size).forgejo
: The name of the database.> ... .pgdump
: Redirects the output to a timestamped backup file. You might be prompted for theforgejo_user
password unless you've set up passwordless access (e.g., via.pgpass
).
- SQLite: Simply copy the database file (since Forgejo is stopped, the file is consistent).
- PostgreSQL (running host or separate container): Use
-
Backup Forgejo Data Directory: Use
tar
orrsync
to archive the./forgejo/data
directory.rsync
can be efficient for subsequent backups as it only copies changes.# Using tar (creates a single compressed archive) tar czf forgejo_data_backup_$(date +%Y%m%d_%H%M%S).tar.gz ./forgejo/data # Using rsync (copies to a backup location, good for incremental) # rsync -avz --delete ./forgejo/data/ /path/to/forgejo_backup_location/
tar czf
: Create (c
), gzip compress (z
), verbose (v
, optional), file (f
).rsync -avz --delete
: Archive mode (a
), verbose (v
), compress (z
), delete files in destination that don't exist in source (--delete
).
-
Backup Configuration Files: Copy
docker-compose.yml
, Nginx configs, etc. -
Restart Forgejo:
-
Store Backups Securely: Move the backup files (
.pgdump
or.db
,.tar.gz
) to a separate, secure location (different server, cloud storage, offline media).
Backup Frequency: Depends on how critical the data is and how frequently it changes. Daily backups are common for active instances.
Online Backup (Caution):
Forgejo's built-in forgejo dump
command can create backups while the instance is running. However, it might temporarily lock the instance, and consistency between a very large filesystem backup and the database dump during high activity can still be a concern. Test thoroughly.
# Run inside the Forgejo container
docker exec forgejo_server sh -c 'cd /data && forgejo dump -c /data/forgejo/conf/app.ini --file /data/forgejo-backup-$(date +%Y%m%d_%H%M%S).zip'
# Then copy the zip file out of the container/volume:
# docker cp forgejo_server:/data/forgejo-backup-....zip /path/to/backups/
Restore Procedure:
Restoring requires reversing the backup process. Always test your restore procedure periodically!
- Prepare Environment: Set up a server with Docker, Nginx (if used), etc. Copy the
docker-compose.yml
and Nginx configs back. - Stop Services: Ensure no Forgejo/database containers are running (
docker compose down
). - Restore Forgejo Data Directory: Extract the
forgejo_data...tar.gz
archive into the correct location (./forgejo/data
) or usersync
to copy back from the backup location. Ensure file permissions and ownership are correct (might needchown
if user IDs differ). - Restore Database:
- PostgreSQL:
- Start only the PostgreSQL container (
docker compose up -d db
). - Create an empty database (if it doesn't exist from the container startup). You might need to drop the existing one first.
- Restore the dump file:
cat /path/to/backups/forgejo_backup_....pgdump | docker exec -i forgejo_postgres pg_restore -U forgejo_user -d forgejo -v
pg_restore
: Command to restore PostgreSQL dumps.-d forgejo
specifies the target database.-v
for verbose output.
- Start only the PostgreSQL container (
- SQLite: Copy the backed-up
.db
file back into the correct location (./forgejo/data/data/forgejo.db
), overwriting any existing file.
- PostgreSQL:
- Start Forgejo:
- Test: Access the Forgejo instance and verify that repositories, users, issues, and settings are restored correctly. Check clone/push functionality.
Workshop Performing a Backup and Simulated Restore
Objective: Perform a complete backup of the running Forgejo instance (using PostgreSQL) and simulate a restore to verify the backup integrity.
Assumptions:
- Forgejo is running with PostgreSQL, managed by Docker Compose (
forgejo
anddb
services). - You are in the
forgejo-server
directory containingdocker-compose.yml
,./forgejo/data
, and./postgres/data
. - You have created at least one repository and user in Forgejo.
Steps:
Part 1: Backup
-
Create a Backup Directory:
- Creates a directory outside the main
forgejo-server
directory to store backups.
- Creates a directory outside the main
-
Stop Services: Stop Forgejo and the database to ensure consistency.
-
Backup PostgreSQL Database:
- We need the
db
container running temporarily to executepg_dump
. Start only the database: - Execute
pg_dump
viadocker exec
:docker exec forgejo_postgres pg_dump -U forgejo_user -Fc forgejo > ../forgejo-backups/forgejo_db_backup_$(date +%Y%m%d_%H%M%S).pgdump
- You might be prompted for the
forgejo_user
password (the one set indocker-compose.yml
).
- You might be prompted for the
- Stop the database container again:
- We need the
-
Backup Forgejo Data Directory: Archive the
./forgejo/data
directory. -
Backup Configuration Files:
-
(Optional Cleanup before Restore Simulation): To simulate a disaster, let's rename the original data directories. Be careful here!
# Ensure containers are stopped (docker compose ps should be empty or show exited containers) mv ./forgejo/data ./forgejo/data_OLD mv ./postgres/data ./postgres/data_OLD
- This makes it seem like the original data is lost.
Part 2: Restore Simulation
-
Prepare Restore Area: Ensure the target directories exist but are empty.
-
Restore Forgejo Data Directory: Extract the data archive.
# Find the data backup file name ls ../forgejo-backups/forgejo_data_backup_*.tar.gz # Extract (replace ... with the actual filename) tar xzf ../forgejo-backups/forgejo_data_backup_....tar.gz -C ./ # Check ownership (adjust if your host UID/GID is not 1000) ls -ld ./forgejo/data # If needed: sudo chown -R 1000:1000 ./forgejo/data
-
Restore PostgreSQL Database:
- Start only the database container. Since we moved
./postgres/data_OLD
, it will initialize a fresh, empty data directory using the environment variables fromdocker-compose.yml
. - Verify the empty database exists:
- Restore the dump file:
# Find the DB backup file name ls ../forgejo-backups/forgejo_db_backup_*.pgdump # Restore (replace ... with actual filename) cat ../forgejo-backups/forgejo_db_backup_....pgdump | docker exec -i forgejo_postgres pg_restore -U forgejo_user -d forgejo -v
- Look for successful restore messages and no major errors.
- Start only the database container. Since we moved
-
Start Forgejo Service:
-
Verify Restoration:
- Check container logs:
docker compose logs forgejo
. Look for successful startup and connection to the restored database. - Access Forgejo via your browser (
https://git.yourdomain.com
). - Log in using your original administrator or user account.
- Verify that your repositories, users, organization (
university-project-x
), issues (if any), and settings are present and appear as they did before the backup. - Try cloning a repository.
- Check container logs:
-
Cleanup (Optional): Once you've confirmed the restore was successful, you can remove the
_OLD
directories if you wish.You can also stop the containers:# Be absolutely sure the restore worked before doing this! # sudo rm -rf ./forgejo/data_OLD # sudo rm -rf ./postgres/data_OLD
docker compose down
.
Conclusion: You have successfully performed an offline backup of your Forgejo instance (configuration, file data, and PostgreSQL database) and simulated a restore process. This exercise highlights the critical steps involved and provides confidence that your backups are usable. Remember to automate backups and store them securely off-site. Regularly testing your restore procedure is essential.
3. Advanced Topics and Integrations
This section explores more advanced features and configurations for your Forgejo instance, including automating tasks with CI/CD, customizing the look and feel, enhancing security further, and migrating from other platforms.
Built-in CI/CD with Forgejo Actions
Forgejo includes a built-in Continuous Integration/Continuous Deployment (CI/CD) system called Forgejo Actions, which is largely compatible with GitHub Actions. This allows you to automate tasks like building code, running tests, checking for code style violations, and deploying applications directly from your Forgejo instance whenever code changes are pushed or pull requests are created.
Core Concepts:
- Workflow: An automated process defined in a YAML file located in your repository under
.forgejo/workflows/
. A repository can have multiple workflows. - Event: A specific activity that triggers a workflow, such as
push
,pull_request
,release
,schedule
, etc. - Job: A set of steps that execute on the same runner. Workflows can contain one or more jobs, which can run sequentially or in parallel.
- Step: An individual task within a job. Steps can run shell commands or use pre-built Actions (reusable units of code).
- Runner (act_runner): An application that listens for available jobs from your Forgejo instance, executes them, and reports the results back. You need to install and register at least one runner for Actions to work. Runners can be installed on the same server as Forgejo or on separate machines (physical, virtual, or Docker containers).
- Action: A reusable piece of code that performs a specific task (e.g.,
actions/checkout@v3
to check out repository code,actions/setup-python@v4
to set up a Python environment). Forgejo Actions can use many actions designed for GitHub Actions.
How it Works:
- You commit a workflow YAML file (e.g.,
.forgejo/workflows/ci.yml
) to your repository. - An event occurs that the workflow is configured to listen for (e.g., a
push
to themain
branch). - Forgejo detects the event and queues the jobs defined in the workflow.
- A registered
act_runner
polls Forgejo, sees the available job, and picks it up. - The runner executes the steps defined in the job (checking out code, running commands, etc.) usually within a temporary environment (often a Docker container).
- The runner sends logs and the final status (success/failure) back to Forgejo.
- Forgejo displays the workflow run status and logs in the "Actions" tab of the repository.
Setting up act_runner
:
The act_runner
needs to be downloaded and run somewhere it can communicate with your Forgejo instance's URL.
- Download
act_runner
: Obtain the latest release binary for your runner's operating system and architecture from theact_runner
releases page (usually linked from Forgejo documentation or found on Codeberg/GitHub). - Register the Runner: Run the
register
command. It will ask for:- Forgejo Instance URL: Your Forgejo
ROOT_URL
(e.g.,https://git.yourdomain.com/
). - Registration Token: Obtain this from Forgejo: Site Administration -> Runners -> Create new runner -> Registration Token, OR from Organization Settings -> Runners, OR User Settings -> Runners. Tokens determine the scope (instance, organization, or user level). Instance runners can run jobs for any repository; org/user runners are restricted.
- Runner Name: A descriptive name (e.g.,
my-local-runner
). - Labels (Optional): Tags used to direct jobs to specific runners (e.g.,
linux,docker,python
). Workflows can specify required labels. - This creates a
.runner
configuration file in theact_runner
directory.
- Forgejo Instance URL: Your Forgejo
- Run the Runner: Start the runner daemon.
- It's recommended to run
act_runner
as a systemd service or within a Docker container for background operation and management.
- It's recommended to run
Example Workflow (.forgejo/workflows/greet.yml
):
name: Greeting Workflow
# Controls when the workflow will run
on:
push: # Trigger on every push event
branches: [ main ] # Only for pushes to the main branch
pull_request: # Trigger on pull request events
branches: [ main ] # Only for PRs targeting the main branch
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "greet"
greet_job:
# The type of runner that the job will run on
runs-on: ubuntu-latest # Use a runner with the 'ubuntu-latest' label (common default)
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Step 1: Print a simple greeting
- name: Say Hello
run: echo "Hello, Forgejo Actions!"
# Step 2: Print the branch or tag ref that triggered the workflow
- name: Show Ref
run: echo "This workflow was triggered by ref: ${{ github.ref }}"
# Step 3: Check out repository code (uses a pre-built Action)
- name: Check out code
uses: actions/checkout@v3 # Use the standard checkout action
# Step 4: List files in the checked-out directory
- name: List Files
run: ls -la
Explanation:
name
: The name displayed in the Forgejo UI.on
: Defines the trigger events (push
tomain
,pull_request
targetingmain
).jobs
: Contains the job definitions.greet_job
: The ID of the job.runs-on: ubuntu-latest
: Specifies that this job needs a runner capable of running "ubuntu-latest" tasks (often means Linux, potentially with Docker). Runners are often labeled this way by default or via configuration.steps
: The sequence of tasks.name
: A descriptive name for the step shown in the UI.run
: Executes a shell command.github.ref
is an example of accessing context variables.uses: actions/checkout@v3
: Uses a pre-defined action to perform a common task (checking out the repository code into the runner's workspace).
When this file is pushed to the main
branch of a repository on your Forgejo instance (and you have a runner registered and running), the workflow will automatically execute.
Workshop Creating a Simple CI Pipeline
Objective: Create a simple Forgejo Actions workflow in your my-first-project
repository that runs when code is pushed, download and register an act_runner
, and observe the workflow execution.
Assumptions:
- Your Forgejo instance is running and accessible via HTTPS (
https://git.yourdomain.com
). - You have the
my-first-project
repository created earlier. - You have
git
installed locally and can push to the repository. - You have shell access to a machine where you can run the
act_runner
(this can be the same server running Forgejo or your local machine, as long as it can reach the Forgejo URL).
Steps:
-
Get Runner Registration Token:
- Log in to Forgejo as an administrator.
- Go to Site Administration -> Runners.
- Under "Create new runner", find the "Registration token". Copy this token. We'll create an instance-level runner.
-
Download and Prepare
act_runner
:- Go to the
act_runner
releases page (search for "forgejo act_runner releases" - it's likely hosted on codeberg.org, e.g.,https://codeberg.org/forgejo/act_runner/releases
). - Download the latest binary appropriate for the OS/Architecture where you will run the runner (e.g.,
act_runner-0.2.6-linux-amd64
). - Open a terminal on the machine where you will run the runner.
- Make the downloaded file executable:
- Go to the
-
Register the Runner:
- Run the registration command in the same directory as the
act_runner
binary:./act_runner register --no-interactive --instance https://git.yourdomain.com --token YOUR_REGISTRATION_TOKEN --name my-first-runner --labels linux,docker
- Replace
https://git.yourdomain.com
with your Forgejo URL. - Replace
YOUR_REGISTRATION_TOKEN
with the token you copied. --no-interactive
: Uses flags instead of prompts.--name
: Gives the runner a name.--labels
: Assigns labels.linux,docker
are common useful labels.
- Replace
- This should create a
.runner
file in the current directory containing the runner's configuration. It might also create a.env
file.
- Run the registration command in the same directory as the
-
Run the Runner:
- Start the runner daemon process:
- The runner will start polling your Forgejo instance for jobs. Keep this terminal window open or run it in the background (e.g., using
nohup ./act_runner daemon &
or setting it up as a systemd service).
-
Verify Runner in Forgejo:
- Go back to the Forgejo UI -> Site Administration -> Runners.
- You should see
my-first-runner
listed, likely with a green circle indicating it's active and polling.
-
Create the Workflow File:
- On your local machine, navigate into your cloned
my-first-project
repository directory. - Create the workflow directory:
- Create a new workflow file using a text editor:
- Paste the following content into the file:
name: Basic Build and Test Simulation on: push: branches: [ main ] # Run on push to main branch pull_request: branches: [ main ] # Run on PRs targeting main branch jobs: build: runs-on: linux # Target our runner using the 'linux' label steps: - name: Checkout code uses: actions/checkout@v3 - name: Simulate Build Step run: | echo "Starting build..." sleep 2 # Simulate time taken echo "Build successful!" - name: Simulate Test Step run: | echo "Running tests..." sleep 3 # Simulate time taken echo "All tests passed!" - name: Print Goodbye run: echo "Workflow finished."
- Save and close the file (
Ctrl+X
,Y
,Enter
).
- On your local machine, navigate into your cloned
-
Commit and Push the Workflow:
- Stage the new workflow file:
- Commit the change:
- Push to Forgejo:
-
Observe Workflow Execution:
- Go to your
my-first-project
repository page in the Forgejo UI. - Click on the "Actions" tab.
- You should see a new workflow run listed for the "Add basic CI workflow" commit. It might initially show as "Queued" or "Running".
- Click on the workflow run name to see the details.
- Click on the
build
job on the left. - You can expand each step ("Checkout code", "Simulate Build Step", etc.) to see the logs generated by the runner in real-time (or after completion).
- The runner terminal (where you ran
./act_runner daemon
) will also show output indicating it picked up and executed the job. - Once all steps complete successfully, the job and the overall workflow run should show a green checkmark (Success).
- Go to your
Conclusion: You have successfully set up Forgejo Actions! You added a workflow file to your repository, registered an act_runner
, and triggered the workflow by pushing code. Forgejo automatically detected the push, assigned the job to your runner, and the runner executed the defined steps. This forms the foundation for automating builds, tests, and deployments within your Forgejo instance.
Customizing Forgejo's Appearance
Forgejo allows customization of its look and feel to better match your branding or personal preferences. This is primarily done by overriding templates and adding custom CSS or static assets.
The custom
Directory:
The key to customization is the custom
directory within Forgejo's data path. In our Docker setup, this corresponds to ./forgejo/data/forgejo/custom
on the host. Forgejo checks this directory for overrides before using its default files.
Common Customizations:
-
Custom CSS:
- Create a CSS file:
./forgejo/data/forgejo/custom/public/css/theme-override.css
- Add your custom CSS rules in this file. For example, to change the header background color:
/* ./forgejo/data/forgejo/custom/public/css/theme-override.css */ body .ui.top.menu { background-color: #2c3e50 !important; /* A dark blue-gray */ } body .ui.top.menu .item { color: #ecf0f1 !important; /* Light gray text */ } body .ui.top.menu .item:hover { background-color: #34495e !important; /* Slightly lighter on hover */ color: #ffffff !important; }
- Forgejo automatically detects and includes this CSS file. You might need to clear your browser cache or do a hard refresh (
Ctrl+Shift+R
orCmd+Shift+R
) to see changes. Sometimes a Forgejo restart (docker compose restart forgejo
) helps ensure it picks up new custom files.
- Create a CSS file:
-
Custom Logo:
- Place your logo file (e.g.,
logo.svg
orlogo.png
) in:./forgejo/data/forgejo/custom/public/img/
- Forgejo will automatically use
custom/public/img/logo.svg
if it exists, otherwisecustom/public/img/logo.png
, falling back to the default if neither custom file is found.
- Place your logo file (e.g.,
-
Custom Favicon:
- Place your
favicon.png
file in:./forgejo/data/forgejo/custom/public/img/favicon.png
- Place your
-
Custom Templates (More Advanced):
- You can override the HTML templates used to render pages. This allows for significant changes to layout and content.
- Find the default template file you want to modify within the Forgejo source code (or a running container). For example, the overall page structure might be in
templates/base/head.tmpl
ortemplates/base/footer.tmpl
. - Copy the original template file to the corresponding path within the
custom
directory on your host (e.g.,./forgejo/data/forgejo/custom/templates/base/footer.tmpl
). - Edit the copied file in the
custom
directory. - Requires Restart: Template changes require restarting the Forgejo container (
docker compose restart forgejo
). - Caution: Overriding templates can make upgrading Forgejo more complex, as you may need to manually update your custom templates if the original templates change significantly in a new version. Use this sparingly and carefully.
-
Custom Landing Page:
- Create
./forgejo/data/forgejo/custom/templates/home.tmpl
. Forgejo will use this instead of the default dashboard/landing page for non-logged-in users.
- Create
-
Adding Static Files:
- Files placed in
./forgejo/data/forgejo/custom/public/
(e.g., custom fonts, images used by your CSS) will be served under/assets/
. For instance,custom/public/fonts/myfont.woff2
would be accessible at/assets/fonts/myfont.woff2
.
- Files placed in
Finding Default Files/Templates:
To know what to override, you can look inside the running Forgejo container or browse the Forgejo source code on Codeberg:
# List default templates inside the container
docker exec forgejo_server ls /usr/local/share/forgejo/templates/base/
# Copy a default template out to use as a base for customization
docker cp forgejo_server:/usr/local/share/forgejo/templates/base/footer.tmpl ./my_custom_footer.tmpl
# Then move ./my_custom_footer.tmpl to ./forgejo/data/forgejo/custom/templates/base/footer.tmpl and edit it.
Workshop Applying a Custom Theme (CSS Override)
Objective: Apply simple CSS customizations to change the header color and add a custom message to the footer of your Forgejo instance.
Assumptions:
- Forgejo is running via Docker Compose.
- You have access to the host directory mapped to Forgejo's data volume (
./forgejo/data
).
Steps:
-
Create Custom CSS Directory:
- Navigate to your
forgejo-server
directory on the host. - Create the necessary directories if they don't exist:
- Navigate to your
-
Create Custom CSS File:
- Use a text editor to create the override file:
- Paste the following CSS rules into the file:
/* Custom Forgejo Theme */ /* Change header background and text color */ body .ui.top.fixed.menu { /* Target the top header menu */ background: linear-gradient(to right, #11998e, #38ef7d); /* Green gradient */ border-bottom: 1px solid #11998e; } body .ui.top.fixed.menu .item, body .ui.top.fixed.menu .item > .text { /* Target menu items and text within them */ color: #ffffff !important; /* White text */ opacity: 0.9; } body .ui.top.fixed.menu .item:hover { background-color: rgba(255, 255, 255, 0.1) !important; /* Slight white overlay on hover */ opacity: 1.0; } /* Add a small custom message before the footer */ #footer::before { content: "Powered by Forgejo | Hosted by My University Dept | "; display: inline; /* Or block if you want it on a new line */ font-size: 0.9em; color: #777; margin-right: 5px; }
- Save and close the file.
-
Restart Forgejo (Optional but recommended): While CSS might be picked up dynamically, restarting ensures Forgejo recognizes the new custom file structure if it wasn't there before.
-
Verify Changes:
- Open your Forgejo instance in your browser (
https://git.yourdomain.com
). - Perform a hard refresh (e.g.,
Ctrl+Shift+R
orCmd+Shift+R
) to bypass browser cache. - The top header bar should now have a green gradient background with white text.
- Scroll down to the bottom of the page. You should see the custom text "Powered by Forgejo | Hosted by My University Dept | " displayed just before the standard footer links ("API", "Website", version number, etc.).
- Open your Forgejo instance in your browser (
Conclusion: You have successfully customized the appearance of your Forgejo instance using a simple CSS override file placed in the custom
directory. This demonstrates how you can easily apply visual themes or branding without modifying Forgejo's core code. For more complex changes, you would explore overriding templates.
Security Hardening Best Practices
While setting up HTTPS and using a reverse proxy significantly improves security, several other measures should be considered for a production-ready Forgejo instance.
-
Regular Updates: Keep Forgejo, your underlying OS, Docker, Nginx, and PostgreSQL updated. Security vulnerabilities are discovered regularly, and updates often contain patches. Subscribe to Forgejo release notifications.
- Forgejo Update (Docker): Update the image tag in
docker-compose.yml
(e.g.,codeberg.org/forgejo/forgejo:7.1
), then rundocker compose pull forgejo
followed bydocker compose up -d forgejo
. Read release notes for potential breaking changes or migration steps. - System Updates:
sudo apt update && sudo apt upgrade -y
(Debian/Ubuntu).
- Forgejo Update (Docker): Update the image tag in
-
Firewall Configuration: Ensure your server's firewall (e.g.,
ufw
,firewalld
) only allows traffic on necessary ports (e.g., 22 for SSH management, 80/443 for Nginx, 2222 for Forgejo SSH). Deny all other incoming traffic by default. -
Fail2Ban: Install and configure Fail2Ban to monitor logs and automatically block IP addresses that show malicious behavior (e.g., multiple failed login attempts, probing for vulnerabilities).
- Monitor Nginx access/error logs for failed logins or suspicious requests.
- Monitor Forgejo's own logs (
./forgejo/data/log/forgejo.log
) for failed logins via the web UI. - Monitor system SSH logs (
/var/log/auth.log
) for failed logins to the server itself and potentially failed Git SSH attempts (though Forgejo handles SSH auth internally, failed connections might hit the SSH daemon on port 2222). - Create custom Fail2Ban filters and jails targeting Forgejo login failures.
-
Strong Passwords & 2FA: Enforce strong password policies for users. Encourage or require the use of Two-Factor Authentication (TOTP supported). Admins can manage 2FA settings in the Admin Panel.
-
SSH Security:
- Use SSH Keys: Disable password authentication for Git over SSH if possible (relies on users using keys).
- Restrict SSH Key Usage (Advanced): Forgejo's SSH server can restrict what commands an SSH key can execute (typically limited to
forgejo-serv
commands for Git operations). This is the default and generally secure. - Secure Host SSH: Secure the server's main SSH daemon (running on port 22 usually) by disabling root login, disabling password authentication (use keys only), and potentially changing the default port.
-
Forgejo Configuration (
app.ini
): Review security-related settings:[service]
:DISABLE_REGISTRATION = true
(highly recommended for private instances),ENABLE_CAPTCHA
,REGISTER_EMAIL_CONFIRM
,DEFAULT_KEEP_EMAIL_PRIVATE = true
,DEFAULT_ALLOW_CREATE_ORGANIZATION = false
(if needed).[security]
:INSTALL_LOCK = true
(should be set automatically after install),SECRET_KEY
(ensure it's long and random, generated during install),REVERSE_PROXY_TRUSTED_PROXIES
(list your reverse proxy's IP, e.g.,127.0.0.1
, if Nginx/Forgejo are on the same host, or Docker network gateway if separated). This ensures Forgejo trusts headers likeX-Forwarded-For
.[admin]
:DISABLE_REGULAR_ORG_CREATION = true
.
-
Reverse Proxy Security Headers: Ensure your Nginx configuration includes security headers like
Strict-Transport-Security
(HSTS),X-Frame-Options
,X-Content-Type-Options
,Referrer-Policy
, and potentiallyContent-Security-Policy
(CSP). Certbot's default Nginx options often include some of these. CSP requires careful configuration to avoid breaking functionality. -
Database Security: Use strong, unique passwords for database users. Configure PostgreSQL to only listen on necessary interfaces (e.g.,
localhost
or the Docker network interface) if it doesn't need to be exposed further. UseSSL_MODE = require
inapp.ini
if connecting to the database over an untrusted network. -
Backup Security: Encrypt your backups and store them securely in a geographically separate location. Ensure access to the backup storage is tightly controlled.
-
Limit Admin Access: Grant administrator privileges sparingly. Use regular user accounts for daily tasks.
-
Monitor Logs: Regularly review Forgejo logs, Nginx logs, database logs, and system logs for any unusual activity or errors.
Workshop Implementing Security Headers and SSH Key Restrictions
Objective: Add essential security headers via the Nginx reverse proxy configuration and understand how Forgejo restricts SSH key usage.
Assumptions:
- You are using Nginx as a reverse proxy configured for HTTPS (from Workshop 2).
- You have shell access to your server with
sudo
privileges. - You have SSH access configured for Git operations with Forgejo.
Part 1: Adding Security Headers in Nginx
-
Edit Nginx Configuration:
-
Locate HTTPS Server Block: Find the
server { ... }
block that listens onlisten 443 ssl http2;
. -
Add/Verify Security Headers: Inside this
server
block (but outside any specificlocation
block unless you want them location-specific), add or ensure the following headers are present. Certbot might have added HSTS already.# Add these lines within the 'server {...}' block for port 443 # Instructs browsers to always connect via HTTPS for 1 year, including subdomains add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # Prevents browser from MIME-sniffing the content type add_header X-Content-Type-Options nosniff always; # Prevents the site from being rendered within an iframe (Clickjacking protection) add_header X-Frame-Options DENY always; # Or SAMEORIGIN if you need framing from same origin # Enables browser's built-in XSS protection filter add_header X-XSS-Protection "1; mode=block" always; # Controls how much referrer info is sent (good default for privacy/security) add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Optional: Content Security Policy (CSP) - Start restrictive and test! # This is a basic example, real-world policies are often more complex. # add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; frame-ancestors 'none';" always; # Warning: Implementing CSP requires careful testing as it can break functionality. # Start without it or use a reporting-only mode first.
- Explanation: Each
add_header
directive adds an HTTP response header. Thealways
parameter ensures the header is added even for error responses. Read documentation for each header to understand its implications fully. HSTS (Strict-Transport-Security
) is particularly important for HTTPS enforcement.
- Explanation: Each
-
Save and Close: Save the configuration file (
Ctrl+X
,Y
,Enter
). -
Test Nginx Configuration:
- Ensure it reports syntax is OK.
-
Reload Nginx: Apply the changes.
-
Verify Headers:
- Open your Forgejo site in your browser.
- Open the browser's Developer Tools (usually F12).
- Go to the "Network" tab.
- Refresh the page (you might need to check "Disable cache").
- Select the main HTML document request (usually the first one for your domain).
- Inspect the "Response Headers" section. You should see the headers you added (
strict-transport-security
,x-content-type-options
,x-frame-options
, etc.). - Alternatively, use an online tool like
securityheaders.com
to scan your domain.
Part 2: Understanding SSH Key Restrictions
This part is informational – Forgejo handles this automatically, but it's good to understand how.
- Examine
authorized_keys
: When you add an SSH public key via the Forgejo UI, Forgejo doesn't typically add it directly to a standard~/.ssh/authorized_keys
file of a system user in the traditional way. Instead, Forgejo manages SSH authentication internally. - Forgejo's Internal SSH Server: The SSH server listening on port 2222 (in our setup) is part of the Forgejo binary itself.
- Key Lookup: When you attempt an SSH connection (e.g.,
git clone ssh://...
), the Forgejo SSH server receives the connection request. It extracts the public key offered by your Git client. - Database Check: Forgejo looks up this public key in its database (the one storing users, repositories, etc.).
- Command Restriction: If the key is found and associated with a valid Forgejo user, Forgejo then internally restricts the commands that this SSH session is allowed to run. It essentially forces the execution of a specific internal command handler (
forgejo-serv key-<key_id>
) which processes Git requests (push/pull). - Result: You cannot use your Git SSH key added to Forgejo to get a general shell login on the server via port 2222, even if the key is valid. The connection is strictly limited to performing Git actions authorized for that key/user within Forgejo.
Verification (Conceptual):
- Try to SSH directly using the Git port and your Forgejo key:
- You should receive an error message similar to:
- This confirms that Forgejo accepted your key but prevented shell access, restricting it to Git operations only.
Conclusion: You have enhanced your instance's security by adding important HTTP security headers via Nginx. You also understand that Forgejo's built-in SSH server inherently restricts SSH keys added through its UI to only perform Git-related actions, preventing unauthorized shell access through the Git SSH port.
Migrating Repositories to Forgejo
Often, you'll want to move existing repositories from other platforms (like GitHub, GitLab, Bitbucket, or another Gitea/Forgejo instance) to your self-hosted Forgejo server. Forgejo offers several ways to do this.
Methods:
-
Migration via UI (Recommended for most cases):
- Forgejo has a built-in migration tool accessible via the UI. Click the "+" icon -> "New Migration".
- Source: Select the platform (GitHub, GitLab, Gitea, Git) or enter a generic Git HTTPS/SSH URL.
- Clone Address: Enter the HTTPS or SSH URL of the repository you want to migrate. For private repositories on platforms like GitHub, you'll likely need to provide an Access Token with repository read permissions.
- Authentication: Enter username/password or access token if required by the source.
- Migration Information: Choose the Forgejo owner (user or org), repository name (can be different from the source), visibility (public/private).
- Migrate Items: Crucially, you can choose what to migrate besides the Git data itself:
- Issues
- Pull Requests
- Milestones
- Labels
- Releases (including assets)
- Wiki
- Benefits: Easiest method, often preserves metadata like issues and PRs (support varies by source platform). Handles LFS objects automatically if configured.
- Limitations: May not work perfectly for all platforms or complex histories. Requires the Forgejo server to be able to reach the source repository URL.
-
Manual Push (Mirroring):
- If you only need the Git history (code and commits) and not issues, PRs, etc., you can manually push an existing local clone to Forgejo.
- Steps:
- (Forgejo UI) Create a new, empty repository on Forgejo (e.g.,
my-migrated-repo
). Do not initialize it with any files. - (Local Machine) Navigate to your existing local clone of the source repository.
- (Local Machine) Add your Forgejo instance as a new remote:
- (Local Machine) Push all branches and tags to the new remote:
- (Forgejo UI) Create a new, empty repository on Forgejo (e.g.,
- Benefits: Simple, reliable for Git data, doesn't require the Forgejo server to access the source.
- Limitations: Does not migrate issues, PRs, releases, wiki, etc.
-
Repository Mirroring:
- Forgejo can automatically mirror repositories from other sources. This keeps your Forgejo copy updated with changes from the original source (pull mirror) or pushes changes from Forgejo back to the original source (push mirror).
- Set up via Repository Settings -> Mirror Settings.
- Use Case: Useful if you want a local copy of an external repository or need to gradually transition while keeping two locations in sync for a period. Can also be used as a one-time import mechanism (create a pull mirror, wait for sync, then disable mirroring).
- Benefits: Keeps repositories synchronized. Can migrate LFS objects.
- Limitations: Can be slightly more complex to set up authentication tokens. Primarily for keeping things in sync, not just a one-time move (though can be used for that).
Considerations:
- Issues/PRs: The UI migration is generally the only way to preserve these. Manual pushes lose this metadata.
- Large Files (LFS): Ensure LFS is configured correctly in your Forgejo
app.ini
before migrating repositories that use LFS. UI migration and mirroring usually handle LFS, but manual pushes require ensuring your local Git LFS client can push to the Forgejo remote. - Authentication: Migrating private repositories often requires Personal Access Tokens (PATs) from the source platform (GitHub, GitLab) with appropriate read permissions.
- User Mapping: Migrated issues/PRs will often show the user who performed the migration as the author unless Forgejo can map usernames/emails from the source to existing Forgejo users.
- Testing: For critical repositories, consider doing a test migration first to ensure metadata and history are transferred as expected.
Workshop Migrating a Repository from GitHub
Objective: Use Forgejo's UI migration feature to import a public repository (e.g., Forgejo's own awesome-forgejo
list) from GitHub into your Forgejo instance, including issues and releases.
Assumptions:
- Your Forgejo instance is running and accessible.
- You are logged in to Forgejo.
- Your Forgejo server can make outbound HTTPS connections to
github.com
.
Steps:
-
Find a Repository to Migrate: We'll use the
awesome-forgejo
repository as an example. Its URL ishttps://github.com/forgejo/awesome-forgejo.git
. -
Start Migration in Forgejo:
- Click the "+" icon in the top right corner of the Forgejo UI.
- Select "New Migration".
-
Select Source:
- Choose "GitHub" from the source options. Alternatively, you could select "Git" and paste the URL. Choosing "GitHub" might enable more specific metadata fetching.
-
Enter Clone Address:
- In the "Clone Address" field, paste the HTTPS URL:
https://github.com/forgejo/awesome-forgejo.git
- In the "Clone Address" field, paste the HTTPS URL:
-
Authentication (Not Needed for Public Repo):
- Since this is a public repository, you can leave the "Auth Username / Email" and "Auth Password / Token" fields blank. If migrating a private repo, you would enter your GitHub username and a Personal Access Token here.
-
Configure Migration:
- Repository Owner: Select your Forgejo username or an organization you own.
- Repository Name: Enter a name for the repository on your instance (e.g.,
imported-awesome-forgejo
). - Visibility: Choose "Public" or "Private" for the migrated repository on your instance.
- Migration Items: Ensure the following are checked (if you want them):
Git Data
(Required)Issues
Pull Requests
Milestones
Labels
Releases
Wiki
(This repo likely doesn't have one, but good practice to check if needed)
- Mirror Repository: Leave this unchecked for a one-time migration.
-
Start Migration:
- Click the "Migrate Repository" button.
-
Monitor Progress:
- Forgejo will start the migration process in the background. You might be redirected to the new repository page, which may initially be empty or show a "Migration in progress" message.
- The time taken depends on the size of the repository and the amount of metadata.
awesome-forgejo
is small and should migrate quickly. - You can check the status in the Site Administration -> System Monitor -> Background Tasks queue if needed, but usually just waiting and refreshing the repository page is sufficient.
-
Verify Migrated Repository:
- Once the migration is complete (the repository page loads normally), verify the content:
- Code: Check if the files and commit history match the original GitHub repository.
- Issues: Go to the "Issues" tab. You should see the issues imported from GitHub. Note that authors might be mapped to your user if the original authors don't exist on your Forgejo instance.
- Pull Requests: Check the "Pull Requests" tab (look at closed PRs).
- Releases: Check the "Releases" tab.
- Once the migration is complete (the repository page loads normally), verify the content:
Conclusion: You have successfully migrated a public repository from GitHub to your self-hosted Forgejo instance using the built-in UI migration tool. You observed that not only the Git history but also associated metadata like issues and releases were transferred, providing a much richer import than a simple manual Git push. This method is highly effective for bringing external projects into your controlled environment.
Conclusion
Congratulations on completing this comprehensive guide to self-hosting Forgejo! You have journeyed from the fundamental concepts of why self-hosting a Git server is beneficial, through the basic installation and configuration using Docker, securing your instance with Nginx and HTTPS, leveraging a robust PostgreSQL database, managing users and permissions effectively, and implementing crucial backup strategies.
Furthermore, you explored advanced capabilities including setting up a CI/CD pipeline with Forgejo Actions, customizing the look and feel of your instance, applying security hardening techniques, and migrating existing repositories from other platforms.
By mastering these steps, you have empowered yourself with:
- Full Control: Your code, your data, your rules, running on your infrastructure.
- Enhanced Privacy & Security: Reduced reliance on third-party services and the ability to implement tailored security measures.
- Customization: The power to adapt Forgejo to your specific needs and workflows.
- Valuable Skills: Practical experience in Linux administration, Docker, Nginx, databases, Git server management, and CI/CD principles.
Self-hosting Forgejo is not just about running a Git server; it's about building a resilient, private, and efficient platform for your software development endeavors, whether for personal projects, university assignments, or organizational collaboration.
Where to Go Next?
- Explore
app.ini
: Dive deeper into the extensive configuration options available inapp.ini
via the official Forgejo Configuration Cheat Sheet documentation. - Advanced Actions: Experiment with more complex Forgejo Actions workflows, including building Docker images, deploying applications, or integrating with external services.
- Federation (ActivityPub): Investigate Forgejo's experimental support for federation using ActivityPub, allowing interaction with other federated instances.
- Monitoring & Alerting: Set up monitoring tools (like Prometheus/Grafana) to track Forgejo's performance and health, and configure alerting for critical issues.
- Contribute: Forgejo is community-driven. Consider contributing back by reporting bugs, suggesting features, improving documentation, or even contributing code.
Remember that the official Forgejo documentation and the community forum/chat are invaluable resources as you continue to use and potentially expand your self-hosted setup. Keep your instance updated, maintain your backups diligently, and enjoy the freedom and control that comes with running your own Git service.