commit 329c33afb125208f85a7a3f0affdff8c9384afae Author: allan Date: Sun Oct 26 10:46:10 2025 +0000 initial commit 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..bff22ea --- /dev/null +++ b/README.md @@ -0,0 +1,217 @@ +# Gitea Multi-Instance for Ubuntu 24.04 Server +[![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)](#) +[![DB](https://img.shields.io/badge/server-mariadb-003545)](#) +[![Server](https://img.shields.io/badge/server-gitea-609926)](#) +[![License](https://img.shields.io/badge/License-MIT-green)](./LICENSE) + +Automated Bash installer script for deploying **multiple isolated Gitea instances** on Ubuntu 24.04 Server. + +This is a **production-focused installer**, not a demo. + +It assumes Nginx and MariaDB are already installed and running. + +--- + +## Related Installers + +If you don’t have the required components, you can use these compatible installers: + +- [Nginx Installer](https://git.x-files.dk/web-server/nginx-ubuntu) +- [MariaDB Installer](https://git.x-files.dk/database-server/mariadb-ubuntu) + +--- + +> **Important** +> Always complete one installation fully (including the `/etc/gitea*/gitea-postinstall` step) before running another. + +--- + +### Download the Script +Clone this repository to your server: + +``` +git clone https://git.x-files.dk/web-application/gitea-ubuntu-multi.git +``` + +``` +cd gitea-ubuntu-multi +``` + +### Usage +Run the script using: + +``` +sudo ./giteainstall -n -p [options] +``` + +### Examples + +The installer automatically detects if socket authentication is active. +Only include `-a` and `-m` if your MariaDB setup does **not** use socket authentication. + +``` +sudo ./giteainstall -n git.example.com -p giteadbpass +sudo ./giteainstall -n git.example.com -p giteadbpass -m rootpwd +sudo ./giteainstall -n git.example.com -p giteadbpass -a admin -m adminpwd +``` + +--- + +### Options + +| Flag | Description | +|------|--------------| +| `-p ` | Gitea database user password | +| `-a ` | Optional MariaDB admin username (defaults to `root` if not specified) | +| `-m ` | MariaDB root or admin password — *required only if socket authentication is disabled* | +| `-h`, `--help` | Show the help screen (reflects detected socket status) | + +--- + +### Logic + +When you run this script: + +- Each Gitea instance created by this script is **fully self-contained**. +- Every instance has: + - Its own **Linux system user** (e.g., `gitea1`, `gitea2`, `gitea3`, …) + - Its own **database** (e.g., `gitea1db`, `gitea2db`, `gitea3db`, …) + - Its own **service** (e.g., `gitea1.service`, `gitea2.service`, …) + - Its own **home directory** (e.g., `/home/gitea1/`, `/home/gitea2/`, …) + - Its own **configuration directory** (e.g., `/etc/gitea1/`, `/etc/gitea2/`, …) + - Its own **data path** (e.g., `/var/lib/gitea1/`, `/var/lib/gitea2/`, …) + - A unique **HTTP port** automatically assigned during post-install + +All instances share the same Gitea binary (`/usr/local/bin/gitea`), +so upgrading Gitea is simply replacing that binary once. + +| Instance | System User | Database | Config Dir | Service | Port | +|-----------|--------------|-----------|-------------|----------|-------| +| 1st | `gitea1` | `gitea1db` | `/etc/gitea1/` | `gitea1.service` | 3001 | +| 2nd | `gitea2` | `gitea2db` | `/etc/gitea2/` | `gitea2.service` | 3002 | +| 3rd | `gitea3` | `gitea3db` | `/etc/gitea3/` | `gitea3.service` | 3003 | + +Each instance is assigned the next available ID automatically. + +--- + +### Customization +Each instance has its own configuration and customization paths: + +| Type | Path | +|------|------| +| Configuration | `/etc/giteaX/app.ini` | +| Custom assets (favicon, logo, etc.) | `/etc/giteaX/custom/public/assets/img/` | +| Custom templates | `/etc/giteaX/custom/templates/` | +| Data & repositories | `/var/lib/giteaX/` | + +--- + +### Configuration +Once the script finishes, open your browser and visit: + +``` +http:// +``` + +and complete the Gitea setup through the web installer form. + +--- + +### Post-install +After the web installer is done run: + +```bash +sudo /etc/gitea*/gitea-postinstall +``` + +This adjusts ports, log levels, upload limits, disables SSH access and other tweaks. + +> **Note** +> The web installer always uses temporary port 3000. +> The postinstall script automatically reconfigures each instance to its permanent port. + +> SSH is disabled by default after post-install (modify `/etc/gitea*/app.ini` if needed). + +--- + +### Nginx Integration + +The generated Gitea configuration file listens on port 80. +To enable HTTPS (port 443), use the example provided [here](https://git.x-files.dk/xtras/nginxsnippets/src/branch/main/hostfiles/gitea.443.conf). + +> **IMPORTANT** +> If you enable HTTPS, update `/etc/giteaX/app.ini` (`ROOT_URL` setting) +> from `http` to `https`, then restart both Nginx and Gitea. + +> **NOTE** +> The file also includes optional caching directives (commented out by default). +> You can enable them to improve load times and performance if needed. +> The lines you are looking for are at the bottom of the generated config file and look like this: +> ``` +> ##### Cache js css static content and open files start ##################### +> # include /etc/nginx/nginxsnippets/cache-open-files.conf; +> # include /etc/nginx/nginxsnippets/cache-statics.conf; +> # include /etc/nginx/nginxsnippets/cache-js-css.conf; +> ##### Cache js css static content and open files stop ###################### +> ``` + +--- + +### Version Handling + +This installer automatically checks the latest **Gitea version** from [https://dl.gitea.com/gitea/version.json](https://dl.gitea.com/gitea/version.json) + +If it fails, it falls back to the version number stored in the **`fallback`** file. That file contains one line, for example: + +``` +1.24.6 +``` + +> **Why a separate file?** +> Keeping the fallback version outside the script avoids polluting the code with constants. +> This keeps updates clean and ensures the installer logic never changes just because a version bump is needed. + +--- + +### Troubleshooting + +**Nginx fails to restart** +Run `nginx -t` and review any syntax errors reported in `/etc/nginx/conf.d/.conf`. + +**Gitea front-end not loading** +Run `systemctl status gitea nginx` and ensure both Gitea and Nginx services are active. + +**Access denied during database creation** +Your MariaDB setup likely does not use socket authentication. +Re-run the installer with the `-m` flag (and optionally `-a`) to provide credentials. + +--- + +### FAQ + +**Q:** Why doesn’t the script ask for a MariaDB password by default? +**A:** Ubuntu 24.04’s MariaDB installation defaults to *socket authentication*, +allowing the local `root` user to connect without a password. +The installer detects this automatically and skips password prompts when applicable. + +**Q:** Can I run the installer multiple times? +**A:** Yes. Database and user creation use `IF NOT EXISTS`, making them safe to re-run. +Existing configuration files are replaced, but no data is deleted. + +**Q:** Does this modify existing Nginx settings? +**A:** No. It adds a standalone host file in `/etc/nginx/conf.d/` +and validates configuration changes before applying them. + +--- + +### 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..7a429d6 --- /dev/null +++ b/fallback @@ -0,0 +1 @@ +1.24.6 diff --git a/giteainstall b/giteainstall new file mode 100755 index 0000000..7b5172a --- /dev/null +++ b/giteainstall @@ -0,0 +1,296 @@ +#!/usr/bin/env bash + +# Author : Allan Christensen +# First Created : 25082022 (DD-MM-YYYY) +# Description : Installs multiple isolated Gitea instances on Ubuntu 24.04 +# License : MIT License (see LICENSE file for details) + +# +# Are we root +# +if [[ $(id -u) -ne 0 ]]; then printf "\nMust be root or use sudo.\n\n" ; exit 1 ; fi + +# +# Port 3000 must be temporarily free for the Gitea web installer +# +giteaport=3000 +if ss -tuln | grep -q ":${giteaport}\b"; then + printf "\nPort %s appears to be in use.\n" "$giteaport" + listener=$(ss -tulpn 2>/dev/null | awk '/:3000\b/ && /LISTEN/ {print $7}' | sed 's/users:(("//; s/",.*//') + if [[ "$listener" == gitea* ]]; then + printf "Detected a running Gitea-related process: %s\n" "$listener" + printf "It looks like another Gitea multi-instance installation has not yet completed.\n" + printf "Please finish its web installer and run its postinstall script before continuing.\n\n" + elif [[ -n "$listener" ]]; then + printf "Process using port 3000 : %s\n" "$listener" + printf "This port is required temporarily by the Gitea web installer.\n" + printf "Please stop or reconfigure that application before running this installer.\n\n" + else + printf "Port 3000 is in use by an unknown process.\n" + printf "Please ensure it is free before running this installer.\n\n" + fi + exit 1 +fi + +# +# Define variables and functions +# +fallbackversion=$( -p [options]\n\n" + printf -- "Options:\n" + printf -- " -a Optional admin username $socketusage\n" + printf -- " -m Optional admin password $socketusage\n" + printf -- " -h | -help | --help Show this help screen\n\n" + printf -- "Examples:\n" + printf -- " sudo ./giteainstall -n git1.example.com -p gitea1dbpwd\n" + printf -- " sudo ./giteainstall -n git2.example.com -p gitea2dbpwd -m rootpwd\n" + printf -- " sudo ./giteainstall -n git3.example.com -p gitea3dbpwd -a admin -m adminpwd\n\n" +} + +serviceno () { printf "\n%s is not running cannot continue...\n\n" "$service"; } +servicedead () { status=$(systemctl is-active "$service"); if [[ "$status" != "active" ]]; then serviceno ; exit ; fi ; } +inputcheck () { [[ -z "$hostname" ]] && { usage ; printf "\nError! Hostname empty.\n\n" ; exit 1 ; } ; [[ -z "$dbpass" ]] && { usage ; printf "\nError! Database password empty.\n\n" ; exit 1 ; } ; } + +# +# If Nginx and MariaDB are not running then die +# +service="mariadb" ; servicedead ; service="nginx" ; servicedead + +# +# Check MariaDB authentication method (socket or not) +# +if mysql -u root -e ";" 2>/dev/null; then socket="SOCKET DETECTED — no need for -a or -m" ; socketusage="SOCKET DETECTED — these flags are not needed" ; socketauth="yes" +else socket="NO SOCKET DETECTED — you must use -a and -m" ; socketusage="NO SOCKET DETECTED — these flags are required" ; socketauth="no" ; fi + +clear + +# +# Check for the latest Gitea version +# +version=$(curl -s "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" + +# +# Configure command line options (safe for set -u) +# +firstarg="${1:-}" +if [[ "$firstarg" == "-help" || "$firstarg" == "--help" ]]; then usage ; exit 0 ; fi +if [[ $# -eq 0 || ! "$firstarg" =~ ^- ]]; then usage ; exit 1 ; fi + +while getopts "n:p:m:a:h" option; do + case "$option" in + n) hostname=$(echo "$OPTARG" | tr '[:upper:]' '[:lower:]');; + p) dbpass="$OPTARG";; + m) mariadbpwd="$OPTARG";; + a) mariadbadmin="$OPTARG";; + h) usage ; exit 0 ;; + \?) printf "Type sudo %s -h for help\n" "$0" ; exit 1 ;; + esac +done + +inputcheck "$hostname" "$dbpass" + +# +# Check for existing Nginx host file with same domain +# +hostcheck=$(grep -r --exclude="giteainstall" --exclude="postinstall" --exclude="README.md" "$hostname" /etc/nginx/ 2>/dev/null || true) +if [[ -n "$hostcheck" ]]; then printf "\nFound existing configuration mentioning %s — aborting to avoid collision.\n\n" "$hostname" ; exit 1 ; fi + +# +# Create Gitea counter logic +# +mkdir -p /var/lib/gitea +user_counter_file="/var/lib/gitea/giteausercount" +[ ! -f "$user_counter_file" ] && printf "0\n" > "$user_counter_file" +current_user=$(cat "$user_counter_file") ; next_user=$((current_user + 1)) +giteauser="gitea$next_user" ; gitea_db="${giteauser}${giteadb}" ; giteaport=$((3000 + next_user)) +printf "%s\n" "$next_user" > "$user_counter_file" + +# +# Download gitea if not already present +# +if [[ ! -f $gitealocation ]]; then + wget --no-verbose "https://dl.gitea.com/gitea/${version}/gitea-${version}-linux-amd64" -O "$gitealocation" || { printf "\nDownload failed. Check your network connection or version string.\n\n" ; exit 1 ; } + chmod 755 "$gitealocation" +fi + +# +# Clone nginx-snippets; if nginx-snippets exists then just pull latest changes +# +nginxsnippets="/etc/nginx/nginx-snippets" ; repo="https://git.x-files.dk/webserver/nginx-snippets.git" +if [[ -d "$nginxsnippets/.git" ]]; then git -C "$nginxsnippets" pull --quiet ; else git clone --quiet "$repo" "$nginxsnippets" ; fi + +# +# Escape special characters in the password for MySQL +# +safe_dbpass=$(printf "%s" "$dbpass" | sed "s/'/''/g") ; mariadbadmin="${mariadbadmin:-root}" + +printf "\nChecking MariaDB access method...\n" +if [[ "$socketauth" == "yes" ]]; then dbmethod="socket" ; printf "Socket authentication detected (root)\n" +elif [[ -n "${mariadbpwd:-}" && -n "${mariadbadmin:-}" ]]; then dbmethod="admin" ; printf "Using admin user authentication (%s)\n" "$mariadbadmin" +else printf "\nERROR: No valid MariaDB authentication method found.\n" ; exit 1 ; fi + +# +# Create Gitea database +# +case "$dbmethod" in + socket) + mysql -u root < /etc/systemd/system/$giteauser.service < "$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,arc-green +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:\$giteaport/" /etc/nginx/conf.d/\$hostname.conf +printf "\\nUpdated Nginx proxy for %s to port %s\\n" "\$hostname" "\$giteaport" +systemctl reload nginx ; systemctl restart \$giteauser +EOF + +chmod 755 "$postscript" + +# +# Gitea postinstall notice +# +postnotice=$(cat <