initial commit

This commit is contained in:
2025-10-26 10:46:10 +00:00
commit 329c33afb1
4 changed files with 535 additions and 0 deletions

21
LICENSE Normal file
View File

@@ -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.

217
README.md Normal file
View File

@@ -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 dont 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 <domain name> -p <gitea database password> [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 <password>` | Gitea database user password |
| `-a <username>` | Optional MariaDB admin username (defaults to `root` if not specified) |
| `-m <password>` | 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://<domain>
```
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/<domain>.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 doesnt the script ask for a MariaDB password by default?
**A:** Ubuntu 24.04s 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)

1
fallback Normal file
View File

@@ -0,0 +1 @@
1.24.6

296
giteainstall Executable file
View File

@@ -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=$(<fallback)
giteadb="db"
gitealocation="/usr/local/bin/gitea"
usage () {
printf -- "\ngiteainstall-multi\n\n"
printf -- "%s\n\n" "$socket"
printf -- "Usage:\n"
printf -- "sudo ./giteainstall -n <gitea domain> -p <gitea database password> [options]\n\n"
printf -- "Options:\n"
printf -- " -a <mariadb admin user> Optional admin username $socketusage\n"
printf -- " -m <mariadb admin password> 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 <<EOF
CREATE DATABASE IF NOT EXISTS $gitea_db;
CREATE USER IF NOT EXISTS '$giteauser'@'localhost' IDENTIFIED BY '${safe_dbpass//\'/\'\\\'\'}';
GRANT ALL PRIVILEGES ON $gitea_db.* TO '$giteauser'@'localhost';
FLUSH PRIVILEGES;
EOF
;;
admin)
mysql -u "$mariadbadmin" -p"$mariadbpwd" <<EOF
CREATE DATABASE IF NOT EXISTS $gitea_db;
CREATE USER IF NOT EXISTS '$giteauser'@'localhost' IDENTIFIED BY '${safe_dbpass//\'/\'\\\'\'}';
GRANT ALL PRIVILEGES ON $gitea_db.* TO '$giteauser'@'localhost';
FLUSH PRIVILEGES;
EOF
;;
esac
#
# Create Gitea temporary Nginx configuration file using port 3000 for web installer
#
cp "$nginxsnippets/hostfiles/gitea-multi.80.conf" /etc/nginx/conf.d/"$hostname".conf
sed -i "s/DOMAIN/$hostname/g" /etc/nginx/conf.d/"$hostname".conf
sed -i "s/GITEAPORT/3000/g" /etc/nginx/conf.d/"$hostname".conf
systemctl restart nginx
#
# Create dedicated Gitea system user and home directory
#
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/{data,indexers,public,log}
chown "$giteauser":"$giteauser" /var/lib/$giteauser/{data,indexers,log}
chmod 750 /var/lib/$giteauser/{data,indexers,log}
mkdir -p /etc/$giteauser/custom/{templates,public/assets/img}
chown -R $giteauser:$giteauser /etc/$giteauser/custom
chmod -R 750 /etc/$giteauser/custom
mkdir -p /etc/$giteauser ; chown root:"$giteauser" /etc/$giteauser ; chmod 770 /etc/$giteauser
#
# Create systemd service
#
cat > /etc/systemd/system/$giteauser.service <<EOF
[Unit]
Description=Gitea Multi-Instance: $giteauser
After=syslog.target network.target
Requires=mariadb.service
[Service]
LimitMEMLOCK=infinity
LimitNOFILE=65535
RestartSec=2s
Type=simple
User=$giteauser
Group=$giteauser
WorkingDirectory=/var/lib/$giteauser/
ExecStart=/usr/local/bin/gitea web -c /etc/$giteauser/app.ini
Restart=always
Environment=USER=$giteauser HOME=/home/$giteauser GITEA_WORK_DIR=/var/lib/$giteauser GITEA_CUSTOM=/etc/$giteauser/custom
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload ; systemctl enable "$giteauser" ; systemctl start "$giteauser"
#
# Create postinstall script
#
postscript="/etc/$giteauser/gitea-postinstall"
cat > "$postscript" <<EOF
#!/usr/bin/env bash
if [[ \$(id -u) -ne 0 ]]; then printf "\\nMust be root or use sudo.\\n\\n" ; exit 1 ; fi
hostname="$hostname"
giteauser="$giteauser"
giteaport="$giteaport"
cp -Rp /etc/\$giteauser/app.ini /etc/\$giteauser/app.ini.orig
sed -i -E "s/^HTTP_PORT *= *3000/HTTP_PORT = \$giteaport/" /etc/\$giteauser/app.ini
sed -i '/gitea-repositories/a MAX_FILES = 500' /etc/\$giteauser/app.ini
sed -i '/gitea-repositories/a FILE_MAX_SIZE = 200' /etc/\$giteauser/app.ini
sed -i 's/LEVEL = info/LEVEL = warn/' /etc/\$giteauser/app.ini
sed -i 's/MODE = console/MODE = file/' /etc/\$giteauser/app.ini
sed -i 's/DISABLE_SSH = false/DISABLE_SSH = true/' /etc/\$giteauser/app.ini
cat >> /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 <<EOF
--------------------------------------------------------------------------------------
NEXT STEP: Go to http://$hostname and complete the initial configuration.
--------------------------------------------------------------------------------------
Database Name : $gitea_db
Database User : $giteauser
Database Password: $dbpass
IMPORTANT: Leave the Gitea port unchanged. The postinstall script will take care this.
--------------------------------------------------------------------------------------
IMPORTANT: Once done from a terminal run the following command to finish up
sudo /etc/$giteauser/gitea-postinstall
The HTTP port and more will be automatically adjusted during the postinstall step.
--------------------------------------------------------------------------------------
EOF
)
printf '%s\n' "$postnotice"
printf "Postinstall script saved at: /etc/%s/gitea-postinstall\n" "$giteauser"
printf "\nAll Done...\n"
#
# End of script
#