Gitea on EverPanel
Posted: Tue Jan 20, 2026 5:18 pm
My friend Danny asked me to look into running Gitea on EverPanel...
The dockerhub file does not play nicely with routing ports to 3000, because the Gitea dockerfile seem to have its own webserver (meaning it would request its own ssl certs), this causes problems.
The solution is simple: Install script and nginx.
As subdomains and domains point towards gptcp1 of everpanel, we will point gptcp1 to port 443 (which our nginx server in docker serve the gitea site from).
I use everbackup/everbackup:latest to get shell access in this example, a better version can be made with the script in it, but I'm just making something fairly quickly here.
An example deploy command:
evdevkit acquire -i everbackup/everbackup:latest--gptcp1--443--gptcp2--22--subdomain--gitea
This deploy command would give you a subdomain of gitea.yournode.tld and that subdomain would magically work after running the install script.
Connect to the SSH on the userport with user: everbackup password default. Make sure to not forget to change password after installation.
create your install script and run it in three simple steps.
1. write nano giteainstall.sh
2. Paste this, save and exit:
3. Write chmod +x giteainstall.sh
4. Write sudo ./giteainstall.sh and use your delegated domain (not the assigned subdomain).
After the installation has finished, the site will simply be up and running, ready to serve your needs!
Keep in mind that you need to use an external smtp server to get emails working.
The dockerhub file does not play nicely with routing ports to 3000, because the Gitea dockerfile seem to have its own webserver (meaning it would request its own ssl certs), this causes problems.
The solution is simple: Install script and nginx.
As subdomains and domains point towards gptcp1 of everpanel, we will point gptcp1 to port 443 (which our nginx server in docker serve the gitea site from).
I use everbackup/everbackup:latest to get shell access in this example, a better version can be made with the script in it, but I'm just making something fairly quickly here.
An example deploy command:
evdevkit acquire -i everbackup/everbackup:latest--gptcp1--443--gptcp2--22--subdomain--gitea
This deploy command would give you a subdomain of gitea.yournode.tld and that subdomain would magically work after running the install script.
Connect to the SSH on the userport with user: everbackup password default. Make sure to not forget to change password after installation.
create your install script and run it in three simple steps.
1. write nano giteainstall.sh
2. Paste this, save and exit:
Code: Select all
#!/usr/bin/env bash
set -euo pipefail
# Gitea + Nginx (SSL terminate with your certs) + Supervisor (NO systemd)
# - Gitea listens on 127.0.0.1:3000
# - Nginx serves https://DOMAIN using your cert/key
# - Supervisor keeps gitea running
#
# Cert/key expected at:
# /contract/cfg/tlscert.pem
# /contract/cfg/tlskey.pem
if [[ $EUID -ne 0 ]]; then
echo "Run as root: sudo bash $0"
exit 1
fi
read -rp "Domain for Gitea (e.g. git.example.com): " DOMAIN
if [[ -z "${DOMAIN}" ]]; then
echo "Domain is required."
exit 1
fi
CERT_FILE="/contract/cfg/tlscert.pem"
KEY_FILE="/contract/cfg/tlskey.pem"
[[ -f "$CERT_FILE" ]] || { echo "Missing cert: $CERT_FILE"; exit 1; }
[[ -f "$KEY_FILE" ]] || { echo "Missing key: $KEY_FILE"; exit 1; }
GITEA_VERSION="${GITEA_VERSION:-1.22.6}"
GITEA_HTTP_PORT="${GITEA_HTTP_PORT:-3000}"
log(){ echo "==> $*"; }
detect_gitea_arch() {
local arch
arch="$(dpkg --print-architecture)"
case "$arch" in
amd64) echo "linux-amd64" ;;
arm64) echo "linux-arm64" ;;
armhf) echo "linux-arm-6" ;;
*) echo "Unsupported architecture: $arch" >&2; exit 1 ;;
esac
}
detect_supervisord_conf_from_ps() {
local line conf
line="$(ps -eo args | awk '/[s]upervisord/ {print; exit}')"
conf=""
if [[ -n "$line" ]]; then
conf="$(awk '
{
for (i=1;i<=NF;i++){
if ($i=="-c" && (i+1)<=NF){print $(i+1); exit}
if ($i ~ /^-c.*/ && length($i)>2){print substr($i,3); exit}
}
}' <<<"$line")"
fi
[[ -n "${conf:-}" && -f "$conf" ]] && echo "$conf" || true
}
choose_supervisord_conf() {
local conf
conf="$(detect_supervisord_conf_from_ps || true)"
if [[ -n "${conf:-}" ]]; then
echo "$conf"; return
fi
if [[ -f /etc/supervisor/supervisord.conf ]]; then echo /etc/supervisor/supervisord.conf; return; fi
if [[ -f /etc/supervisord.conf ]]; then echo /etc/supervisord.conf; return; fi
mkdir -p /etc/supervisor/conf.d /var/log/supervisor /var/run
cat >/etc/supervisor/supervisord.conf <<'EOF'
[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700
[supervisord]
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
childlogdir=/var/log/supervisor
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[include]
files = /etc/supervisor/conf.d/*.conf
EOF
echo /etc/supervisor/supervisord.conf
}
ensure_supervisord_running() {
local conf="$1"
if pgrep -x supervisord >/dev/null 2>&1; then
return 0
fi
if command -v service >/dev/null 2>&1; then
service supervisor start >/dev/null 2>&1 || true
fi
if pgrep -x supervisord >/dev/null 2>&1; then
return 0
fi
if command -v supervisord >/dev/null 2>&1; then
supervisord -c "$conf"
fi
pgrep -x supervisord >/dev/null 2>&1 || {
echo "Failed to start supervisord."
echo "Try: supervisord -c $conf"
exit 1
}
}
supervisorctl_with_conf() {
local conf="$1"; shift
supervisorctl -c "$conf" "$@"
}
get_supervisor_include_dir_or_add() {
local conf="$1"
local include_glob include_dir
include_glob="$(awk '
BEGIN{ininc=0}
/^\s*\[include\]\s*$/ {ininc=1; next}
/^\s*\[/ {ininc=0}
ininc && tolower($0) ~ /^\s*files\s*=/ {
sub(/^[^=]*=/,""); gsub(/^[ \t]+|[ \t]+$/,"");
print; exit
}' "$conf" || true)"
if [[ -z "${include_glob:-}" ]]; then
# append include section (safe, simple)
mkdir -p /etc/supervisor/conf.d
cp -a "$conf" "${conf}.bak.$(date +%s)" || true
cat >>"$conf" <<'EOF'
[include]
files = /etc/supervisor/conf.d/*.conf
EOF
include_glob="/etc/supervisor/conf.d/*.conf"
fi
# take first token if multiple globs listed
include_glob="${include_glob%% *}"
include_dir="$(dirname "$include_glob")"
mkdir -p "$include_dir"
echo "$include_dir"
}
reload_nginx() {
nginx -t
# graceful reload if possible; else try service restart
nginx -s reload 2>/dev/null || (command -v service >/dev/null 2>&1 && service nginx restart) || true
}
log "Installing packages..."
apt-get update
apt-get install -y --no-install-recommends \
ca-certificates curl wget git sqlite3 \
nginx supervisor
log "Creating gitea user and directories..."
if ! id -u gitea >/dev/null 2>&1; then
adduser --system --group --home /home/gitea --shell /bin/bash gitea
fi
mkdir -p /var/lib/gitea/{custom,data,log}
mkdir -p /etc/gitea
mkdir -p /var/log/gitea
chown -R gitea:gitea /var/lib/gitea /etc/gitea /var/log/gitea
chmod 750 /etc/gitea
GITEA_ARCH="$(detect_gitea_arch)"
log "Downloading Gitea ${GITEA_VERSION} (${GITEA_ARCH})..."
wget -O /usr/local/bin/gitea \
"https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-${GITEA_ARCH}"
chmod +x /usr/local/bin/gitea
log "Writing /etc/gitea/app.ini ..."
cat >/etc/gitea/app.ini <<EOF
APP_NAME = Gitea
RUN_USER = gitea
RUN_MODE = prod
[server]
DOMAIN = ${DOMAIN}
SSH_DOMAIN = ${DOMAIN}
HTTP_ADDR = 127.0.0.1
HTTP_PORT = ${GITEA_HTTP_PORT}
; External URL is HTTPS (nginx terminates TLS), internal is HTTP
ROOT_URL = https://${DOMAIN}/
PROTOCOL = http
ENABLE_REVERSE_PROXY = true
[database]
DB_TYPE = sqlite3
PATH = /var/lib/gitea/data/gitea.db
[repository]
ROOT = /var/lib/gitea/data/gitea-repositories
[log]
MODE = file
LEVEL = Info
ROOT_PATH = /var/lib/gitea/log
[security]
INSTALL_LOCK = false
EOF
chown gitea:gitea /etc/gitea/app.ini
chmod 640 /etc/gitea/app.ini
log "Writing nginx site config..."
cat >/etc/nginx/sites-available/gitea.conf <<EOF
server {
listen 80;
listen [::]:80;
server_name ${DOMAIN};
return 301 https://\$host\$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ${DOMAIN};
ssl_certificate ${CERT_FILE};
ssl_certificate_key ${KEY_FILE};
location / {
proxy_pass http://127.0.0.1:${GITEA_HTTP_PORT};
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
# "proxified SSL" headers:
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host \$host;
proxy_set_header X-Forwarded-Port 443;
client_max_body_size 100M;
proxy_read_timeout 120s;
proxy_connect_timeout 120s;
}
}
EOF
ln -sf /etc/nginx/sites-available/gitea.conf /etc/nginx/sites-enabled/gitea.conf
rm -f /etc/nginx/sites-enabled/default || true
reload_nginx
SUP_CONF="$(choose_supervisord_conf)"
log "Using supervisord config: $SUP_CONF"
ensure_supervisord_running "$SUP_CONF"
INCLUDE_DIR="$(get_supervisor_include_dir_or_add "$SUP_CONF")"
log "Supervisor include dir: $INCLUDE_DIR"
log "Writing supervisor program: ${INCLUDE_DIR}/gitea.conf"
cat >"${INCLUDE_DIR}/gitea.conf" <<EOF
[program:gitea]
command=/usr/local/bin/gitea web --config /etc/gitea/app.ini
directory=/var/lib/gitea
user=gitea
autostart=true
autorestart=true
startsecs=5
stopsignal=INT
environment=HOME="/home/gitea",USER="gitea",GITEA_WORK_DIR="/var/lib/gitea"
stdout_logfile=/var/log/gitea/stdout.log
stderr_logfile=/var/log/gitea/stderr.log
stdout_logfile_maxbytes=10MB
stderr_logfile_maxbytes=10MB
stdout_logfile_backups=5
stderr_logfile_backups=5
EOF
log "Reloading supervisor programs..."
supervisorctl_with_conf "$SUP_CONF" reread
supervisorctl_with_conf "$SUP_CONF" update
supervisorctl_with_conf "$SUP_CONF" restart gitea >/dev/null 2>&1 || supervisorctl_with_conf "$SUP_CONF" start gitea
log "Status:"
supervisorctl_with_conf "$SUP_CONF" status || true
cat <<EOF
DONE, You can now access gitea with either domain:gptcp1, your assigned subdomain or assigned domain.
Logs:
Gitea: /var/log/gitea/stderr.log and /var/log/gitea/stdout.log
EOF4. Write sudo ./giteainstall.sh and use your delegated domain (not the assigned subdomain).
After the installation has finished, the site will simply be up and running, ready to serve your needs!
Keep in mind that you need to use an external smtp server to get emails working.