Skip to content

Instantly share code, notes, and snippets.

@TheyCallMeLinux
Created April 26, 2026 18:26
Show Gist options
  • Select an option

  • Save TheyCallMeLinux/ef1a46c5b3ab00bc1b8ab8a9885b253a to your computer and use it in GitHub Desktop.

Select an option

Save TheyCallMeLinux/ef1a46c5b3ab00bc1b8ab8a9885b253a to your computer and use it in GitHub Desktop.
Deploy Forgejo on Alma Linux 9 (mariadb)
#!/bin/bash
set -euo pipefail
############################
# VARIABLES
############################
DB_MODE="local"
DB_HOST="127.0.0.1"
DB_NAME="forgejo"
DB_USER="forgejo"
DB_PASS="CHANGEME"
APP_URL="https://forgejo.example.com"
SERVER_NAME="forgejo.example.com"
HTTP_BIND="127.0.0.1"
HTTP_PORT="3000"
SSH_PORT="22"
APP_NAME="example Forgejo"
FORGEJO_VERSION="15.0.0"
DISABLE_REGISTRATION="true"
EMAIL_DOMAIN_ALLOWLIST="gmail.com"
REQUIRE_SIGNIN_VIEW="true"
EXTERNAL_REGISTRATION="true"
REGISTER_EMAIL_CONFIRM="true"
ENABLE_NOTIFY_MAIL="true"
ENABLE_CAPTCHA="true"
RUN_MODE="prod"
SELINUX_MODE="permissive"
SWAP_SIZE="4G"
SWAPPINESS="10"
CERT_COUNTRY="CA"
CERT_STATE="ON"
CERT_CITY="Toronto"
CERT_ORG="exampleCorp"
CERT_UNIT="IT"
MAIL_ENABLED="true"
SMTP_ADDR="smtp.gmail.com"
SMTP_PORT="465"
SMTP_USER="example@gmail.com"
SMTP_PASS="APP PASS WORD"
MAIL_FROM="example@gmail.com"
SMTP_PROTOCOL="smtps"
SSL_CERT="/etc/pki/tls/certs/forgejo.crt"
SSL_KEY="/etc/pki/tls/private/forgejo.key"
############################
# LOGGING
############################
log() {
echo "[DEPLOY] $1"
}
trap 'log "Deployment failed at line $LINENO"' ERR
############################
# SWAP + MEMORY TUNING
############################
if [ ! -f /swapfile ]; then
log "Creating swap"
fallocate -l ${SWAP_SIZE} /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=4096
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
sysctl vm.swappiness=${SWAPPINESS}
fi
############################
# SELINUX
############################
log "Configuring SELinux"
case "$SELINUX_MODE" in
disabled)
setenforce 0 || true
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
;;
permissive)
setenforce 0 || true
sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config
;;
enforcing)
setenforce 1 || true
sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
;;
esac
############################
# SYSTEM UPDATE
############################
log "Updating system"
dnf -y update
############################
# PACKAGES
############################
log "Installing packages"
dnf_safe() {
for i in {1..3}; do
dnf -y "$@" && break
sleep 5
done
}
dnf -y install epel-release
dnf_safe install git curl wget unzip nano tar sqlite
dnf_safe install firewalld
dnf_safe install httpd mod_ssl
dnf_safe install policycoreutils-python-utils
systemctl daemon-reexec
systemctl enable --now httpd
############################
# FIREWALL
############################
systemctl enable --now firewalld httpd
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-port=3000/tcp
firewall-cmd --reload
############################
# DATABASE (optional)
############################
if [ "$DB_MODE" = "local" ]; then
log "Installing MariaDB"
dnf -y install mariadb-server
systemctl enable --now mariadb
until mysqladmin ping --silent; do sleep 2; done
mysql -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME};"
mysql -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';"
mysql -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';"
mysql -e "FLUSH PRIVILEGES;"
fi
############################
# FORGEJO INSTALL
############################
log "Installing Forgejo"
cd /tmp
wget https://codeberg.org/forgejo/forgejo/releases/download/v${FORGEJO_VERSION}/forgejo-${FORGEJO_VERSION}-linux-amd64
install -m 0755 forgejo-${FORGEJO_VERSION}-linux-amd64 /usr/local/bin/forgejo
############################
# USER + DIRS
############################
groupadd --system forgejo || true
useradd --system \
--home /var/lib/forgejo \
--gid forgejo \
--shell /bin/bash \
forgejo || true
mkdir -p /var/lib/forgejo/{custom,data,log}
chown -R forgejo:forgejo /var/lib/forgejo
############################
# CONFIG
############################
log "Creating Forgejo config"
mkdir -p /var/lib/forgejo/custom/conf
SECRET_KEY=$(openssl rand -hex 32)
INTERNAL_TOKEN=$(openssl rand -hex 32)
cat > /var/lib/forgejo/custom/conf/app.ini <<EOF
APP_NAME = ${APP_NAME}
RUN_MODE = ${RUN_MODE}
[server]
DOMAIN = ${SERVER_NAME}
ROOT_URL = ${APP_URL}/
HTTP_ADDR = ${HTTP_BIND}
HTTP_PORT = ${HTTP_PORT}
DISABLE_SSH = false
SSH_PORT = ${SSH_PORT}
START_SSH_SERVER = false
[mailer]
ENABLED = ${MAIL_ENABLED}
PROTOCOL = ${SMTP_PROTOCOL}
SMTP_ADDR = ${SMTP_ADDR}
SMTP_PORT = ${SMTP_PORT}
FROM = ${MAIL_FROM}
USER = ${SMTP_USER}
PASSWD = ${SMTP_PASS}
[database]
DB_TYPE = mysql
HOST = ${DB_HOST}:3306
NAME = ${DB_NAME}
USER = ${DB_USER}
PASSWD = ${DB_PASS}
[security]
INSTALL_LOCK = false
SECRET_KEY = ${SECRET_KEY}
INTERNAL_TOKEN = ${INTERNAL_TOKEN}
PASSWORD_HASH_ALGO = argon2
[service]
DISABLE_REGISTRATION = ${DISABLE_REGISTRATION}
REQUIRE_SIGNIN_VIEW = ${REQUIRE_SIGNIN_VIEW}
ENABLE_CAPTCHA = ${ENABLE_CAPTCHA}
ALLOW_ONLY_EXTERNAL_REGISTRATION = ${EXTERNAL_REGISTRATION}
REGISTER_EMAIL_CONFIRM = ${REGISTER_EMAIL_CONFIRM}
ENABLE_NOTIFY_MAIL = ${ENABLE_NOTIFY_MAIL}
[repository]
DEFAULT_PRIVATE = private
[session]
PROVIDER = file
[log]
MODE = file
LEVEL = Info
ROOT_PATH = /var/lib/forgejo/log
EOF
chown -R forgejo:forgejo /var/lib/forgejo/custom
chmod 750 /var/lib/forgejo/custom
chmod 640 /var/lib/forgejo/custom/conf/app.ini
############################
# SYSTEMD
############################
log "Creating service"
cat > /etc/systemd/system/forgejo.service <<EOF
[Unit]
Description=Forgejo
After=network.target
[Service]
User=forgejo
Group=forgejo
WorkingDirectory=/var/lib/forgejo
ExecStart=/usr/local/bin/forgejo web
Restart=always
RestartSec=2s
Environment=USER=forgejo
Environment=HOME=/var/lib/forgejo
Environment=GITEA_WORK_DIR=/var/lib/forgejo
Environment=APP_DATA_PATH=/var/lib/forgejo/data
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable forgejo
systemctl start forgejo
############################
# SSL
############################
log "Generating self-signed cert"
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout ${SSL_KEY} \
-out ${SSL_CERT} \
-subj "/C=${CERT_COUNTRY}/ST=${CERT_STATE}/L=${CERT_CITY}/O=${CERT_ORG}/OU=${CERT_UNIT}/CN=${SERVER_NAME}"
chmod 600 /etc/pki/tls/private/forgejo.key
############################
# APACHE PROXY
############################
log "Configuring Apache"
cat > /etc/httpd/conf.d/forgejo.conf <<EOF
<VirtualHost *:80>
ServerName ${SERVER_NAME}
Redirect / https://${SERVER_NAME}/
</VirtualHost>
<VirtualHost *:443>
ServerName ${SERVER_NAME}
SSLEngine on
SSLCertificateFile /etc/pki/tls/certs/forgejo.crt
SSLCertificateKeyFile /etc/pki/tls/private/forgejo.key
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
RequestHeader set X-Forwarded-Proto "https"
</VirtualHost>
EOF
setsebool -P httpd_can_network_connect 1
systemctl restart httpd
############################
# CRON
############################
cat > /etc/cron.d/forgejo-health <<EOF
*/5 * * * * root curl -fs http://127.0.0.1:3000/ || systemctl restart forgejo
0 2 * * * root dnf -y update --security
EOF
############################
# DONE
############################
log "Deployment complete"
echo "======================================"
echo " Forgejo is ready"
echo " URL: ${APP_URL}"
echo "======================================"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment