feat(blog): add migrating to forgejo with ansible blog article
All checks were successful
/ all (push) Successful in 13s
All checks were successful
/ all (push) Successful in 13s
This commit is contained in:
parent
6039e9eb3b
commit
692f68cbec
1 changed files with 374 additions and 0 deletions
374
content/blog/ansible/forgejo.md
Normal file
374
content/blog/ansible/forgejo.md
Normal file
|
@ -0,0 +1,374 @@
|
|||
---
|
||||
title: 'Migrating git.adyxax.org from gitolite and cgit to Forgejo'
|
||||
description: 'How I am deploying Forgejo with Ansible'
|
||||
date: '2025-03-25'
|
||||
tags:
|
||||
- 'Ansible'
|
||||
- 'Forgejo'
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Earlier this month I migrated git.adyxax.org from a combination of
|
||||
[gitolite](https://gitolite.com/gitolite/index.html) and
|
||||
[cgit](https://git.zx2c4.com/cgit/about/) to [Forgejo](https://forgejo.org/). 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:
|
||||
|
||||
``` yaml
|
||||
---
|
||||
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.
|
||||
|
||||
``` yaml
|
||||
---
|
||||
- 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:
|
||||
|
||||
``` nginx
|
||||
###############################################################################
|
||||
# \_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:
|
||||
|
||||
``` ini
|
||||
###############################################################################
|
||||
# \_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`:
|
||||
|
||||
``` 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:
|
||||
|
||||
``` yaml
|
||||
---
|
||||
- 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]({{< ref "gitolite-cgit.md" >}}). 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!
|
Loading…
Add table
Reference in a new issue