Migrating git.adyxax.org from gitolite and cgit to Forgejo
2025-03-25 - How I am deploying Forgejo with Ansible
Tags: ansible Forgejo
Introduction
Earlier this month I migrated git.adyxax.org from a combination of gitolite and cgit to Forgejo. My main motivation is to reduce my dependency on GitHub to the strict minimum while still being able to accept issues or other contributions in a user friendly way.
Ansible role
Meta
The meta/main.yaml
contains the role dependencies:
---
dependencies:
- role: 'borg'
- role: 'nginx'
- role: 'postgresql'
Tasks
The tasks/main.yaml
does most of the heavy lifting thanks to roles that I
presented in earlier articles. Contrary to most applications I self host,
Forgejo does not run inside a container but on the host operating system. The
reason is that it is easier to allow git access over SSH this way.
---
- name: 'Install Forgejo dependencies'
package:
name:
- 'git'
- 'git-lfs'
- name: 'Download Forgejo {{ versions.forgejo.tag }}'
get_url:
url: 'https://codeberg.org/forgejo/forgejo/releases/download/v{{ versions.forgejo.tag }}/forgejo-{{ versions.forgejo.tag }}-linux-amd64'
dest: '/usr/local/bin/forgejo'
mode: '0555'
notify: 'restart forgejo'
- name: 'Create git group on server'
group:
name: 'git'
system: 'yes'
- name: 'Create git user on server'
user:
name: 'git'
group: 'git'
shell: '/bin/sh'
home: '/srv/git'
createhome: 'yes'
system: 'yes'
password: '*'
- name: 'Make Forgejo configuration directory'
file:
path: '/etc/forgejo'
owner: 'root'
group: 'git'
mode: '0550'
state: 'directory'
- include_role:
name: 'postgresql'
tasks_from: 'database'
vars:
postgresql:
name: 'forgejo'
- name: 'Deploy Forgejo configuration file'
template:
src: 'app.ini'
dest: '/etc/forgejo/'
owner: 'root'
group: 'git'
mode: '0440'
notify: 'restart forgejo'
- name: 'Make Forgejo data directory'
file:
path: '/srv/forgejo'
owner: 'git'
group: 'git'
mode: '0750'
state: 'directory'
- name: 'Deploy Forgejo systemd service unit'
copy:
src: 'forgejo.service'
dest: '/etc/systemd/system/'
owner: 'root'
group: 'root'
mode: '0440'
notify: 'systemctl daemon-reload'
- include_role:
name: 'nginx'
tasks_from: 'vhost'
vars:
vhost:
name: 'git'
path: 'roles/forgejo/files/nginx-vhost.conf'
- include_role:
name: 'borg'
tasks_from: 'client'
vars:
client:
jobs:
- name: 'data'
command_to_pipe: "su - git -c '/usr/local/bin/forgejo dump --config=/etc/forgejo/app.ini --file=- --type=tar'"
- name: 'postgres'
command_to_pipe: "su - postgres -c '/usr/bin/pg_dump -b -c -C -d forgejo'"
name: 'forgejo'
server: '{{ forgejo.borg }}'
- name: 'Start Forgejo and activate it on boot'
service:
name: 'forgejo'
enabled: true
state: 'started'
Files
Here is the nginx vhost file, fairly straightforward:
###############################################################################
# \_o< WARNING : This file is being managed by ansible! >o_/ #
# ~~~~ ~~~~ #
###############################################################################
server {
listen 80;
listen [::]:80;
server_name git.adyxax.org;
location / {
return 308 https://$server_name$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name git.adyxax.org;
location / {
proxy_pass http://127.0.0.1:8087;
proxy_set_header Connection $http_connection;
proxy_set_header Upgrade $http_upgrade;
client_max_body_size 512M;
}
ssl_certificate adyxax.org.fullchain;
ssl_certificate_key adyxax.org.key;
}
Here is my forgejo.service
systemd unit file:
###############################################################################
# \_o< WARNING : This file is being managed by ansible! >o_/ #
# ~~~~ ~~~~ #
###############################################################################
[Unit]
Description=Forgejo (Beyond coding. We forge.)
After=syslog.target
After=network.target
Wants=postgresql.service
After=postgresql.service
[Service]
# Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that
# LimitNOFILE=524288:524288
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/srv/forgejo/
ExecStart=/usr/local/bin/forgejo web --config /etc/forgejo/app.ini
Restart=always
[Install]
WantedBy=multi-user.target
Templates
I have a single template for my app.ini
:
###############################################################################
# \_o< WARNING : This file is being managed by ansible! >o_/ #
# ~~~~ ~~~~ #
###############################################################################
APP_NAME = git.adyxax.org
APP_SLOGAN =
RUN_MODE = prod
RUN_USER = git
WORK_PATH = /srv/forgejo/gitea
[server]
APP_DATA_PATH = /srv/forgejo/gitea
DOMAIN = git.adyxax.org
SSH_DOMAIN = git.adyxax.org
HTTP_ADDR = 127.0.0.1
HTTP_PORT = 8087
ROOT_URL = https://git.adyxax.org/
DISABLE_SSH = false
SSH_PORT = 22
LFS_START_SERVER = true
LFS_JWT_SECRET = {{ ansible_local.forgejo.lfs_jwt_secret }}
OFFLINE_MODE = true
LANDING_PAGE = explore
SSH_USER = git
[database]
PATH =
DB_TYPE = postgres
HOST = 127.0.0.1:5432
NAME = forgejo
USER = forgejo
PASSWD = {{ ansible_local.postgresql_forgejo.password }}
LOG_SQL = false
SCHEMA =
SSL_MODE = disable
[security]
INSTALL_LOCK = true
SECRET_KEY =
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = *
INTERNAL_TOKEN = {{ ansible_local.forgejo.internal_token }}
PASSWORD_HASH_ALGO = pbkdf2_hi
DISABLE_QUERY_AUTH_TOKEN = true
[repository]
ROOT = /srv/forgejo/git/repositories
DEFAULT_REPO_UNITS = repo.code,repo.issues,repo.pulls
DEFAULT_PUSH_CREATE_PRIVATE = true
ENABLE_PUSH_CREATE_USER = true
ENABLE_PUSH_CREATE_ORG = true
[repository.local]
LOCAL_COPY_PATH = /srv/forgejo/gitea/tmp/local-repo
[repository.upload]
TEMP_PATH = /srv/forgejo/gitea/uploads
[indexer]
ISSUE_INDEXER_PATH = /srv/forgejo/gitea/indexers/issues.bleve
[session]
PROVIDER_CONFIG = /srv/forgejo/gitea/sessions
PROVIDER = file
[picture]
AVATAR_UPLOAD_PATH = /srv/forgejo/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /srv/forgejo/gitea/repo-avatars
[attachment]
PATH = /srv/forgejo/gitea/attachments
[log]
MODE = console
LEVEL = warn
ROOT_PATH = /srv/forgejo/gitea/log
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = true
DEFAULT_KEEP_EMAIL_PRIVATE = false
DEFAULT_ALLOW_CREATE_ORGANIZATION = false
DEFAULT_ENABLE_TIMETRACKING = false
NO_REPLY_ADDRESS = noreply.localhost
[lfs]
PATH = /srv/forgejo/git/lfs
[mailer]
ENABLED = false
[openid]
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false
[cron.update_checker]
ENABLED = true
[repository.pull-request]
DEFAULT_MERGE_STYLE = merge
[repository.signing]
DEFAULT_TRUST_MODEL = committer
[oauth2]
JWT_SECRET = {{ ansible_local.forgejo.jwt_secret }}
[other]
SHOW_FOOTER_VERSION = false
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
SHOW_FOOTER_POWERED_BY = false
[actions]
ENABLED = true
Handlers
This role relies on two handlers:
---
- name: 'restart forgejo'
service:
name: 'forgejo'
state: 'restarted'
- name: 'systemctl daemon-reload'
shell:
cmd: 'systemctl daemon-reload'
Authentication sources
Since one of my goals is to have this instance open to contributions, I chose to allow people to register accounts on my instance. Hopefully I do not have to deal with to much spam but if it comes to that I will just close it or activate the manual confirmation of new accounts.
Besides Forgejo’s internal authentication, I activated oauth2 authentication from Google and GitHub. Both were fairly straightforward to configure for me, but they require to be familiar with quite a lot of things that would require a dedicated article! If you are interested drop me an email or a toot and I will detail the process of setting this up.
Oauth2 via Google required me to configure a GCP project to create a client auth platform client. For GitHub the process is similar but via a GitHub oauth2 application.
Email notifications
I decided to completely forgo email notifications for now. I am wary of a forge becoming an easy spamming machine and am not quite ready to risk it yet.
Conclusion
I self hosted a Gitea between 2020 and 2022 and eventually grew tired of it. The irony is not lost on me that I am migrating in reverse now, but I must say that Forgejo looks a lot more polished and responsive than Gitea ever was. For now, I am quite happy with Forgejo!