From 4dd6474a7852653fac4907d13dfee28e9e811285 Mon Sep 17 00:00:00 2001 From: allan Date: Fri, 19 Dec 2025 15:31:57 +0100 Subject: [PATCH] latest commit --- LICENSE | 21 +++ README.md | 221 ++++++++++++++++++++++++++++ fallback | 1 + giteainstall | 395 +++++++++++++++++++++++++++++++++++++++++++++++++++ last-tested | 6 + 5 files changed, 644 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 fallback create mode 100755 giteainstall create mode 100644 last-tested diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3eaaa81 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Allan Christensen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e1f71cf --- /dev/null +++ b/README.md @@ -0,0 +1,221 @@ +# Gitea Multi‑Instance for Ubuntu 24.04 +[![OS](https://img.shields.io/badge/ubuntu-24.04-E95420)](#) +[![Shell](https://img.shields.io/badge/shell-bash-121011)](#) +[![WebServer](https://img.shields.io/badge/server-nginx-009639)](#) +[![MariaDB](https://img.shields.io/badge/db-mariadb-003545)](#) +[![MySQL](https://img.shields.io/badge/db-mysql-4479A1)](#) +[![Gitea](https://img.shields.io/badge/app-gitea-609926)](#) +[![License](https://img.shields.io/badge/License-MIT-green)](./LICENSE) + +Install multiple Gitea instances on Ubuntu 24.04 server. + +This is not a demo environment. +This is a production-focused installer built to scale cleanly and update centrally. + +## Why this installer exists +Running several Gitea instances on one server should be easy. This script makes multi-instance hosting repeatable. + +## What this installer does +✔ Auto-numbers instances (gitea1, gitea2 …) +✔ Creates isolated DB + service per instance +✔ Assigns final ports safely post-install +✔ Includes robots + sitemap generation +✔ Shared binary — one upgrade updates all + +## What this installer does *NOT* do +It won’t stop you from running the script without reading the documentation like there’s no tomorrow. +Skip the README, and whatever happens next is your headache, not a bug report. + +--- + +## Requirements + +You must already have: + +✔ Nginx installed +✔ MariaDB/MySQL running +✔ Port **3000 free during install** (temporary only) + +### Optional Installers (if you need them — saves you some googling) + +- [Install Nginx + PHP-FPM on Ubuntu](https://git.x-files.dk/webserver/nginx-ubuntu) +- [Install MariaDB on Ubuntu](https://git.x-files.dk/database/mariadb-ubuntu) +- [Install MySQL on Ubuntu](https://git.x-files.dk/database/mysql-ubuntu) + +--- + +## 1. Download + +``` +git clone https://git.x-files.dk/webapps/gitea-ubuntu-multi.git +``` + +``` +cd gitea-ubuntu-multi +``` + +--- + +## 2. Install a new instance + +``` +sudo ./giteainstall-multi -n -p [options] +``` + +### Examples + +Socket auth present: +``` +sudo ./giteainstall-multi -n git1.example.com -p gitea1pwd +``` + +No socket → use DB credentials: +``` +sudo ./giteainstall-multi -n git2.example.com -p pass2 -m rootpwd +``` + +### Flags + +| Option | Meaning | +|---|---| +| `-p` password | DB password for the new Gitea instance | +| `-m` password | Admin/root MariaDB password *(if no socket)* | +| `-a` username | Admin DB user *(default = root)* | +| `-h` | Help screen | + +--- + +## How instances are created + +Each install assigns the **next instance number automatically**: + +| Instance | User | DB | Config | Service | Final Port | +|---|---|---|---|---|---| +| 1 | gitea1 | gitea1db | /etc/gitea1 | gitea1.service | 3001 | +| 2 | gitea2 | gitea2db | /etc/gitea2 | gitea2.service | 3002 | +| 3 | gitea3 | gitea3db | /etc/gitea3 | gitea3.service | 3003 | + +Temporary installer UI runs at `http://domain:3000` +Final port assigned **after postinstall**. + +--- + +## Complete setup + +1. Visit: +``` +http:// +``` + +2. Finish the web installer WITHOUT changing port. + +3. Run the postinstall unique to this instance: + +``` +sudo /etc/giteaX/gitea-postinstall +``` +(`X` = instance number) + +That step: + +✔ moves Gitea from 3000 → final assigned port +✔ configures logging, UI, paging, themes +✔ disables SSH by default +✔ updates Nginx proxy target + +--- + +## File Layout (auto-increment per instance) + +When first installed, paths look like this: + +**Instance 1** + +| Path | Purpose | +|---|---| +| `/etc/gitea1/app.ini` | Main config file | +| `/etc/gitea1/gitea-postinstall` | Postinstall script | +| `/var/lib/gitea1/` | Data, repos, indexers, logs | +| `/var/lib/gitea1/custom/templates/` | UI overrides | +| `/var/lib/gitea1/custom/public/assets/img/` | Logos + branding | +| `/var/lib/gitea1/custom/public/` | robots + sitemap *(optional)* | +| `/usr/local/bin/gitea` | Shared binary | + +**Instance 2 (identical structure, next number auto-assigned)** + +| Path | Purpose | +|---|---| +| `/etc/gitea2/app.ini` | Main config file | +| `/etc/gitea2/gitea-postinstall` | Postinstall script | +| `/var/lib/gitea2/` | Data, repos, indexers, logs | +| `/var/lib/gitea2/custom/templates/` | UI overrides | +| `/var/lib/gitea2/custom/public/assets/img/` | Logos + branding | +| `/var/lib/gitea2/custom/public/` | robots + sitemap *(optional)* | +| `/usr/local/bin/gitea` | Shared binary | + +> Every new install increments automatically (`gitea3`, `gitea4`, `gitea5`…) + +> Important (but optional): +> `robots.txt` and `sitemap.xml` are supported automatically by the Nginx configuration being created, +> however the installer does not generate these files. Create them manually in the directory listed above. + +--- + +## HTTPS + +Use the TLS template here: + +https://git.x-files.dk/webserver/nginx-snippets/src/branch/main/hostfiles/gitea.443.conf + +Then update in `/etc/giteaX/app.ini`: + +``` +ROOT_URL = https:// +``` + +Restart: + +``` +systemctl restart giteaX nginx +``` + +--- + +## Version Handling + +Script checks latest from: + +``` +https://dl.gitea.com/gitea/version.json +``` + +Fallback file contains static version: + +``` +fallback +1.25.2 +``` + +--- + +## Troubleshooting + +| Issue | Cause | Fix | +|---|---|---| +| Nginx restart fails | Bad config | `nginx -t` | +| DB denied | No socket | Add `-m` (and `-a`) | +| Blank web UI | Service down | `systemctl status giteaX` | +| Next instance reusing port | Postinstall not run | Run `/etc/giteaX/gitea-postinstall` | + +--- + +### More Information + +More guides and documentation can be found on [wiki.x-files.dk](https://wiki.x-files.dk) + +--- + +### License +Licensed under the [MIT License](./LICENSE). + +--- diff --git a/fallback b/fallback new file mode 100644 index 0000000..61b813d --- /dev/null +++ b/fallback @@ -0,0 +1 @@ +1.25.2 diff --git a/giteainstall b/giteainstall new file mode 100755 index 0000000..44fb957 --- /dev/null +++ b/giteainstall @@ -0,0 +1,395 @@ +#!/usr/bin/env bash + +# Author : Allan Christensen +# First Created : 15-09-2022 (DD-MM-YYYY) +# Description : Installs multiple isolated Gitea instances on Ubuntu 24.04 +# License : MIT License + +# +# Are we root +# +if [[ $(id -u) -ne 0 ]]; then echo "" && echo "Must be root or use sudo" && echo "" ; exit ; fi + +# +# Detect database engine (MariaDB or MySQL) +# +if systemctl list-unit-files | grep -q '^mariadb\.service'; then + db_engine="mariadb" + db_service="mariadb" +elif systemctl list-unit-files | grep -q '^mysql\.service'; then + db_engine="mysql" + db_service="mysql" +else + printf "\nNo supported database server found.\n\n" + exit 1 +fi + +# +# Check required services +# +phpfpm=$(systemctl list-unit-files --type=service | awk '/php[0-9]+\.[0-9]+-fpm\.service/ {sub(".service","",$1); print $1; exit}') +for svc in nginx "$db_service"; do systemctl is-active --quiet "$svc" || { printf "\n%s is not running, cannot continue...\n\n" "${svc^}" ; exit 1 ; }; done + +# +# Detect socket authentication (local root access) +# +if mysql -u root -e ";" 2>/dev/null; then + socketauth="yes" +else + socketauth="no" +fi + +# +# Variables +# +fallbackversion=$(/dev/null | awk -v port=":${installer_port}\\b" '$0 ~ port && /LISTEN/ {print $7}' | sed 's/users:(("//; s/",.*//') + if [[ -n "$listener" ]]; then + printf "Process using port %s : %s\n" "$installer_port" "$listener" + fi + printf "\nThis port is required temporarily by the Gitea web installer.\n" + printf "Please free port %s before running this installer.\n\n" "$installer_port" + exit 1 +fi + +# +# Usage +# +usage() { + printf -- "\ngiteainstall multi instance\n\n" + printf -- "Database engine detected: %s\n\n" "$db_engine" + + if [[ "$socketauth" == "yes" ]]; then + printf -- "LOCAL ROOT ACCESS DETECTED — no need for -a or -m\n\n" + printf -- "Installs multiple Gitea instances using local socket authentication.\n\n" + printf -- "Usage:\n" + printf -- " sudo ./giteainstall -n -p \n\n" + printf -- "Example:\n" + printf -- " sudo ./giteainstall -n git1.example.com -p gitea1dbpwd\n\n" + else + printf -- "NO LOCAL ROOT ACCESS — you must use -a and -m\n\n" + printf -- "Installs multiple Gitea instances using database admin credentials.\n\n" + printf -- "Usage:\n" + printf -- " sudo ./giteainstall -n -p -a -m \n\n" + printf -- "Examples:\n" + printf -- " sudo ./giteainstall -n git2.example.com -p gitea2dbpwd -a root -m rootpwd\n" + printf -- " sudo ./giteainstall -n git3.example.com -p gitea3dbpwd -a admin -m adminpwd\n\n" + fi + + printf -- "Options:\n" + printf -- " -h | -help | --help Show this help screen\n\n" +} + +# +# Let's go +# +clear + +# +# Help traps +# +firstarg="${1:-}" +if [[ "$firstarg" == "-help" || "$firstarg" == "--help" ]]; then usage ; exit 0 ; fi +if [[ $# -eq 0 || ! "$firstarg" =~ ^- ]]; then usage ; exit 1 ; fi + +# +# Configure command line options +# +while getopts "n:p:a:m:h" option; do + case "$option" in + n) hostname=$(echo "$OPTARG" | tr '[:upper:]' '[:lower:]');; + p) dbpass="$OPTARG";; + a) db_admin_user="$OPTARG";; + m) db_admin_pwd="$OPTARG";; + h) usage ; exit 0 ;; + \?) printf "Type sudo %s -h for help\n" "$0" ; exit 1 ;; + esac +done + +# +# Validate input +# +if [[ -z "${hostname:-}" || -z "${dbpass:-}" ]]; then + usage + printf "\nERROR: Both -n (domain) and -p (database password) are required.\n\n" + exit 1 +fi + +# +# Normalise DB admin defaults +# +db_admin_user="${db_admin_user:-root}" + +# +# If no socket auth, require admin password +# +if [[ "$socketauth" == "no" && -z "${db_admin_pwd:-}" ]]; then + usage + printf "\nERROR: No local root access detected — you must provide -a and -m.\n\n" + exit 1 +fi + +# +# Ensure required tools +# +for tool in curl git; do dpkg -s "$tool" &>/dev/null || apt install -y -qq "$tool" ; done + +# +# Check for the latest Gitea version +# +version=$(curl -fsSL https://dl.gitea.com/gitea/version.json | grep -oP '"version"\s*:\s*"\K[^"]+') +if [[ -z "$version" ]]; then + printf "Could not determine latest version. Falling back to version %s\n\n" "$fallbackversion" + version="$fallbackversion" +fi +printf "\nUsing Gitea version: %s\n" "$version" + +# +# Download gitea binary if not already present (non-interactive) +# +if [[ ! -x "$gitealocation" ]]; then + curl -fL "https://dl.gitea.com/gitea/${version}/gitea-${version}-linux-amd64" -o "$gitealocation" \ + || { printf "\nDownload failed.\n\n" ; exit 1 ; } + chmod 755 "$gitealocation" +fi + +# +# Clone nginx-snippets; if it exists then pull latest +# +if [[ -d "$nginxsnippets/.git" ]]; then + git -C "$nginxsnippets" pull --quiet +else + git clone --quiet "$nginxrepo" "$nginxsnippets" +fi + +# +# Auto-increment instance number (gitea1, gitea2, ...) +# +mkdir -p /var/lib/gitea +counter_file="/var/lib/gitea/giteausercount" +[[ ! -f "$counter_file" ]] && printf "0\n" > "$counter_file" +current=$(cat "$counter_file") +next=$((current + 1)) +printf "%s\n" "$next" > "$counter_file" + +giteauser="gitea${next}" +gitea_db="${giteauser}${giteadb_suffix}" +final_port=$((3000 + next)) + +# +# Basic collision protection +# +if id "$giteauser" &>/dev/null; then + printf "\nERROR: User %s already exists — refusing to continue.\n\n" "$giteauser" + exit 1 +fi + +if [[ -d "/var/lib/$giteauser" || -d "/etc/$giteauser" ]]; then + printf "\nERROR: Instance paths already exist for %s — refusing to continue.\n\n" "$giteauser" + exit 1 +fi + +if [[ -f "/etc/nginx/conf.d/${hostname}.conf" ]]; then + printf "\nERROR: A configuration file already exists for %s.\n" "$hostname" + printf "Refusing to overwrite existing site.\n\n" + exit 1 +fi + +# +# Create Gitea system user +# +adduser --system --group --disabled-password --shell /bin/bash --home /home/$giteauser --gecos 'Git Version Control' $giteauser +mkdir -p /home/$giteauser/.ssh +chown -R $giteauser:$giteauser /home/$giteauser +chmod 700 /home/$giteauser/.ssh + +# +# Create Gitea data structure +# +mkdir -p /var/lib/$giteauser/{custom,data,indexers,public,log} +chown "$giteauser":"$giteauser" /var/lib/$giteauser/{data,indexers,log} +chmod 750 /var/lib/$giteauser/{data,indexers,log} +mkdir -p /var/lib/$giteauser/custom/{templates,public/assets/img} + +# +# Create /etc/$giteauser and its custom directory +# +mkdir -p /etc/$giteauser/custom +chown root:"$giteauser" /etc/$giteauser +chmod 770 /etc/$giteauser +chown -R "$giteauser":"$giteauser" /etc/$giteauser/custom +chmod -R 750 /etc/$giteauser/custom + +# +# Create database and user +# +printf "\nCreating database: %s\n" "$gitea_db" +if [[ "$socketauth" == "yes" ]]; then + mysql -u root < /etc/systemd/system/$giteauser.service < final port and locks down settings) +# +postscript="/etc/$giteauser/gitea-postinstall" +cat > "$postscript" <> /etc/\$giteauser/app.ini <<'INNER_EOF' +[ui.admin] +USER_PAGING_NUM = 50 +REPO_PAGING_NUM = 50 +NOTICE_PAGING_NUM = 25 +ORG_PAGING_NUM = 25 + +[ui.user] +USER_PAGING_NUM = 50 +REPO_PAGING_NUM = 50 +NOTICE_PAGING_NUM = 25 +ORG_PAGING_NUM = 25 + +[ui] +THEMES = gitea-auto, gitea-light, gitea-dark +EXPLORE_PAGING_DEFAULT_SORT = alphabetically + +[other] +SHOW_FOOTER_POWERED_BY = false +SHOW_FOOTER_VERSION = false +SHOW_FOOTER_TEMPLATE_LOAD_TIME = false +ENABLE_FEED = false +INNER_EOF + +# Update Nginx proxy to final assigned port +sed -i "s/127\\.0\\.0\\.1:3000/127.0.0.1:\$final_port/" /etc/nginx/conf.d/\$hostname.conf +systemctl reload nginx + +# Restart Gitea instance +systemctl restart \$giteauser + +# Remove postinstall script once it has run +rm -f "$postscript" +EOF + +chmod 755 "$postscript" + +# +# Web installer + postinstall notice +# +dbhost="127.0.0.1" +[[ "$socketauth" == "yes" ]] && dbhost="localhost" + +clear +cat <