Initial commit

Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
Raghav
2026-04-16 09:04:22 +05:30
commit bb68b6b9f2
11 changed files with 617 additions and 0 deletions

23
.env.example Normal file
View File

@ -0,0 +1,23 @@
# Host + network
GITEA_DOMAIN=git.bhatfamily.in
GITEA_ROOT_URL=https://git.bhatfamily.in/
GITEA_HTTP_PORT=3000
GITEA_SSH_PORT=2222
TLS_EMAIL=admin@bhatfamily.in
# Storage (host path)
GITEA_BASE_PATH=/media/rbhat/DATA/gitea
# Timezone + permissions
TZ=Asia/Kolkata
PUID=1000
PGID=1000
# Database
POSTGRES_USER=gitea
POSTGRES_PASSWORD=change-me-strong-password
POSTGRES_DB=gitea
# Gitea secrets (set strong values before internet exposure)
GITEA_SECRET_KEY=change-me-gitea-secret-key
GITEA_INTERNAL_TOKEN=change-me-gitea-internal-token

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.env
*.log

91
README.md Normal file
View File

@ -0,0 +1,91 @@
# Gitea Homelab Automation (`git.bhatfamily.in`)
Automated Docker-based setup for a self-hosted Gitea server with PostgreSQL, persistent storage at `/media/rbhat/DATA/gitea`, and lifecycle scripts for install, test, and uninstall.
## What this repository provides
- `docker-compose.yml` for:
- `gitea/gitea:1.24.2`
- `postgres:16-alpine`
- optional TLS reverse proxy (`caddy:2.10-alpine`, profile: `tls`)
- Idempotent lifecycle scripts:
- `scripts/install.sh`
- `scripts/test.sh`
- `scripts/uninstall.sh`
- Environment template: `.env.example`
- Troubleshooting and network/DNS notes in `docs/`
## Layout
- Host storage root: `/media/rbhat/DATA/gitea`
- Gitea data volume: `/media/rbhat/DATA/gitea/gitea-data`
- Repository root (host): `/media/rbhat/DATA/gitea/gitea-data/git/repositories`
- PostgreSQL data: `/media/rbhat/DATA/gitea/postgres`
- Caddy data/config: `/media/rbhat/DATA/gitea/caddy-data`, `/media/rbhat/DATA/gitea/caddy-config`
## Prerequisites
- Docker + Docker Compose plugin installed
- `curl` installed
- `ufw` optional (if active, scripts add/remove rules for Gitea ports)
- Sudo access to manage firewall rules
## Quick start (baseline, no TLS profile)
1. Copy and edit environment values:
- `cp .env.example .env`
- Change at least:
- `POSTGRES_PASSWORD`
- `GITEA_SECRET_KEY`
- `GITEA_INTERNAL_TOKEN`
2. Install/start stack:
- `./scripts/install.sh`
3. Validate setup:
- `./scripts/test.sh`
4. Open Gitea UI:
- `http://localhost:3000` (or your configured HTTP port)
## Quick start (TLS reverse proxy profile)
1. Ensure `.env` has correct values:
- `GITEA_DOMAIN=git.bhatfamily.in`
- `GITEA_ROOT_URL=https://git.bhatfamily.in/`
- `TLS_EMAIL=<your-email>` (used by Caddy for ACME account contact)
2. Ensure DNS + router/NAT are configured first (see `docs/cloudflare-networking.md`).
3. Install with TLS profile:
- `./scripts/install.sh --with-tls --open-public-web`
4. Test TLS profile (strict):
- `./scripts/test.sh --with-tls`
5. If DNS/cert is still propagating, run non-blocking external check:
- `./scripts/test.sh --with-tls --allow-pending-external`
6. Access:
- `https://git.bhatfamily.in`
## Uninstall
- Stop and remove containers, keep data:
- `./scripts/uninstall.sh`
- Stop and remove containers including TLS profile:
- `./scripts/uninstall.sh --with-tls`
- Remove added 80/443 firewall rules too (if added with install flag):
- `./scripts/uninstall.sh --with-tls --close-public-web`
- Stop and remove containers and delete persistent data:
- `./scripts/uninstall.sh --with-tls --purge-data`
- Non-interactive full teardown:
- `./scripts/uninstall.sh --with-tls --purge-data --purge-images --close-public-web --yes`
## Port defaults
- Host HTTP: `3000` -> container `3000`
- Host SSH: `2222` -> container `22`
- TLS profile ports: `80`, `443` -> Caddy
## Firewall behavior
When UFW is active:
- install always adds:
- `allow <GITEA_HTTP_PORT>/tcp` (comment: `Gitea HTTP`)
- `allow <GITEA_SSH_PORT>/tcp` (comment: `Gitea SSH`)
- install with `--open-public-web` also adds:
- `allow 80/tcp` (comment: `Gitea TLS HTTP-01`)
- `allow 443/tcp` (comment: `Gitea TLS HTTPS`)
- uninstall always removes Gitea HTTP/SSH rules
- uninstall with `--close-public-web` removes 80/443 rules
## Cloudflare and home network changes
See `docs/cloudflare-networking.md` for complete instructions.
## Troubleshooting
See `docs/troubleshooting.md` for diagnostics and common fixes.
## Backup basics
- Backup application data:
- `/media/rbhat/DATA/gitea/gitea-data`
- Backup PostgreSQL data:
- `/media/rbhat/DATA/gitea/postgres`
- If TLS profile used, backup Caddy state too:
- `/media/rbhat/DATA/gitea/caddy-data`
- `/media/rbhat/DATA/gitea/caddy-config`
For consistent backups, stop containers first:
- `docker compose --env-file .env -f docker-compose.yml down`
Then archive directories and restart with `./scripts/install.sh` (or with `--with-tls`).

19
caddy/Caddyfile Normal file
View File

@ -0,0 +1,19 @@
{
email {$TLS_EMAIL}
}
{$GITEA_DOMAIN} {
encode zstd gzip
reverse_proxy gitea:3000
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
:80 {
redir https://{$GITEA_DOMAIN}{uri} permanent
}

74
docker-compose.yml Normal file
View File

@ -0,0 +1,74 @@
services:
gitea:
image: gitea/gitea:1.24.2
container_name: gitea
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
env_file:
- .env
environment:
USER_UID: ${PUID}
USER_GID: ${PGID}
GITEA__server__DOMAIN: ${GITEA_DOMAIN}
GITEA__server__ROOT_URL: ${GITEA_ROOT_URL}
GITEA__server__SSH_DOMAIN: ${GITEA_DOMAIN}
GITEA__server__SSH_PORT: ${GITEA_SSH_PORT}
GITEA__server__START_SSH_SERVER: "false"
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: postgres:5432
GITEA__database__NAME: ${POSTGRES_DB}
GITEA__database__USER: ${POSTGRES_USER}
GITEA__database__PASSWD: ${POSTGRES_PASSWORD}
GITEA__security__INSTALL_LOCK: "true"
GITEA__security__SECRET_KEY: ${GITEA_SECRET_KEY}
GITEA__security__INTERNAL_TOKEN: ${GITEA_INTERNAL_TOKEN}
ports:
- "${GITEA_HTTP_PORT}:3000"
- "${GITEA_SSH_PORT}:22"
volumes:
- ${GITEA_BASE_PATH}/gitea-data:/data
healthcheck:
test: ["CMD", "wget", "--spider", "--quiet", "http://localhost:3000/api/healthz"]
interval: 20s
timeout: 5s
retries: 10
start_period: 30s
postgres:
image: postgres:16-alpine
container_name: gitea-postgres
restart: unless-stopped
env_file:
- .env
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- ${GITEA_BASE_PATH}/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
start_period: 20s
caddy:
image: caddy:2.10-alpine
container_name: gitea-caddy
restart: unless-stopped
profiles: ["tls"]
env_file:
- .env
depends_on:
gitea:
condition: service_healthy
ports:
- "80:80"
- "443:443"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ${GITEA_BASE_PATH}/caddy-data:/data
- ${GITEA_BASE_PATH}/caddy-config:/config

View File

@ -0,0 +1,52 @@
# Cloudflare DNS + Home Network Setup for `git.bhatfamily.in`
## Goal
Expose local Gitea securely from home network while preserving Git-over-SSH support.
## Recommended Cloudflare DNS records
Create DNS records in Cloudflare for zone `bhatfamily.in`:
1. `A` record
- Name: `git`
- Content: your home public IPv4
- Proxy status: **DNS only** (gray cloud)
2. Optional `AAAA` record
- Name: `git`
- Content: your home public IPv6
- Proxy status: **DNS only**
## Why DNS-only is recommended
This setup uses direct inbound routing for both HTTPS and custom SSH (`2222` by default). DNS-only avoids Cloudflare proxy protocol limitations around direct SSH forwarding.
## Router/NAT rules
Create forwards to this server's LAN IP:
- TCP 80 -> `<server_lan_ip>:80` (ACME challenge + redirect)
- TCP 443 -> `<server_lan_ip>:443` (HTTPS via Caddy)
- TCP 2222 -> `<server_lan_ip>:2222` (Git SSH)
## Firewall alignment
If UFW is active, use install flag to open public web ports:
- `./scripts/install.sh --with-tls --open-public-web`
To close those later:
- `./scripts/uninstall.sh --with-tls --close-public-web`
## ISP constraints check
Some ISPs block inbound ports. Validate from outside your network:
- `curl -I https://git.bhatfamily.in`
- `nc -vz git.bhatfamily.in 2222`
If blocked, use alternate routing (VPN/tunnel) or ISP-compatible ports.
## Recommended hardening
- Keep strong secrets in `.env`
- Restrict SSH source ranges if practical
- Keep containers patched (`docker compose pull` and recreate)
- Add off-host backups for gitea/postgres/caddy data directories
## Post-cutover verification checklist
Run these after DNS/NAT/firewall updates to confirm end-to-end readiness:
1. DNS resolution
- `dig +short git.bhatfamily.in A`
2. HTTPS response and redirect chain
- `curl -I https://git.bhatfamily.in`
- `curl -I http://git.bhatfamily.in`
3. Certificate validity/issuer
- `openssl s_client -connect git.bhatfamily.in:443 -servername git.bhatfamily.in </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates`
4. Git SSH port reachability
- `nc -vz git.bhatfamily.in 2222`
5. Stack self-check
- `./scripts/test.sh --with-tls`
If any check fails during first-time propagation, run:
- `./scripts/test.sh --with-tls --allow-pending-external`
Then re-run strict checks once DNS/certificate propagation completes.

60
docs/troubleshooting.md Normal file
View File

@ -0,0 +1,60 @@
# Troubleshooting
## 1) Containers fail to start
- Check compose status:
- `docker compose --env-file .env -f docker-compose.yml ps`
- If TLS profile enabled:
- `docker compose --env-file .env -f docker-compose.yml --profile tls ps`
- Inspect logs:
- `docker compose --env-file .env -f docker-compose.yml logs gitea`
- `docker compose --env-file .env -f docker-compose.yml logs postgres`
- `docker compose --env-file .env -f docker-compose.yml --profile tls logs caddy`
## 2) Gitea health endpoint fails
- Local probe:
- `curl -v http://localhost:3000/api/healthz`
- If port changed, use your `GITEA_HTTP_PORT`.
- Confirm mapping:
- `docker compose --env-file .env -f docker-compose.yml ps`
## 3) Database connection errors
- Confirm PostgreSQL health:
- `docker compose --env-file .env -f docker-compose.yml ps postgres`
- Re-check `.env` values:
- `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`
- Look for auth failures in postgres logs.
## 4) Permission issues under `/media/rbhat/DATA/gitea`
- Ensure current user can read/write this path.
- If needed:
- `sudo chown -R rbhat:rbhat /media/rbhat/DATA/gitea`
- `sudo chmod -R u+rwX /media/rbhat/DATA/gitea`
## 5) Firewall blocks access
- Check active rules:
- `sudo ufw status`
- Expected allows:
- `<GITEA_HTTP_PORT>/tcp`
- `<GITEA_SSH_PORT>/tcp`
- `80/tcp` and `443/tcp` if TLS profile is internet-exposed
## 6) TLS cert not issuing
- Ensure `git.bhatfamily.in` resolves publicly to your home WAN IP.
- Ensure inbound TCP 80 and 443 are forwarded to this host.
- Ensure `TLS_EMAIL` is set in `.env`.
- Verify Caddy logs for ACME failures:
- `docker compose --env-file .env -f docker-compose.yml --profile tls logs caddy`
## 7) Port conflicts on 80/443
- Check listeners:
- `ss -tulpen | grep -E '(:80\s|:443\s)'`
- Stop conflicting services or disable TLS profile until resolved.
## 8) DNS resolves but service unreachable
- Verify router forwarding targets correct LAN IP.
- Verify host listening:
- `ss -tulpen | grep -E '3000|2222|80|443'`
- Test from external network (mobile hotspot) to avoid NAT loopback confusion.
## 9) TLS tests fail during propagation
- Strict mode (default) fails until DNS/routing/cert trust is ready:
- `./scripts/test.sh --with-tls`
- Temporary non-blocking mode:
- `./scripts/test.sh --with-tls --allow-pending-external`
## 10) Reset stack while keeping data
- `./scripts/uninstall.sh --with-tls`
- `./scripts/install.sh --with-tls --open-public-web`
## 11) Full clean rebuild
- `./scripts/uninstall.sh --with-tls --purge-data --purge-images --close-public-web --yes`
- Re-run install

71
scripts/install.sh Executable file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/lib.sh"
WITH_TLS=false
OPEN_PUBLIC_WEB=false
for arg in "$@"; do
case "$arg" in
--with-tls) WITH_TLS=true ;;
--open-public-web) OPEN_PUBLIC_WEB=true ;;
*) die "Unknown argument: $arg" ;;
esac
done
log "Starting Gitea install"
require_cmd docker
require_cmd curl
load_env
if [[ "${WITH_TLS}" == "true" ]]; then
: "${TLS_EMAIL:?TLS_EMAIL is required in .env when using --with-tls}"
fi
mkdir -p "${GITEA_BASE_PATH}" "${GITEA_BASE_PATH}/gitea-data" "${GITEA_BASE_PATH}/postgres" "${GITEA_BASE_PATH}/backups" "${GITEA_BASE_PATH}/caddy-data" "${GITEA_BASE_PATH}/caddy-config"
log "Pulling images"
if [[ "${WITH_TLS}" == "true" ]]; then
compose --profile tls pull
else
compose pull
fi
log "Starting containers"
if [[ "${WITH_TLS}" == "true" ]]; then
compose --profile tls up -d
else
compose up -d
fi
wait_for_gitea_health
apply_firewall_rules "${OPEN_PUBLIC_WEB}"
cat <<MSG
Install complete.
Local access:
- Web UI: http://localhost:${GITEA_HTTP_PORT}
- SSH for Git: ssh://git@localhost:${GITEA_SSH_PORT}
Expected repository root on host:
${GITEA_BASE_PATH}/gitea-data/git/repositories
MSG
if [[ "${WITH_TLS}" == "true" ]]; then
cat <<TLSMSG
TLS profile enabled (Caddy).
- HTTPS endpoint (after DNS + router config): https://${GITEA_DOMAIN}
- ACME contact email: ${TLS_EMAIL}
- Optional firewall for 80/443 was $( [[ "${OPEN_PUBLIC_WEB}" == "true" ]] && printf 'enabled' || printf 'not changed' )
TLSMSG
fi
cat <<NEXT
Next:
1) Run scripts/test.sh$( [[ "${WITH_TLS}" == "true" ]] && printf ' --with-tls' || true )
2) Complete DNS/router/Cloudflare steps documented in README.md
NEXT

103
scripts/lib.sh Executable file
View File

@ -0,0 +1,103 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
ENV_FILE="${REPO_ROOT}/.env"
ENV_EXAMPLE_FILE="${REPO_ROOT}/.env.example"
COMPOSE_FILE="${REPO_ROOT}/docker-compose.yml"
log() {
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}
die() {
printf 'ERROR: %s\n' "$*" >&2
exit 1
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"
}
create_env_if_missing() {
if [[ ! -f "${ENV_FILE}" ]]; then
log "No .env found; creating .env from template"
cp "${ENV_EXAMPLE_FILE}" "${ENV_FILE}"
fi
}
load_env() {
[[ -f "${ENV_EXAMPLE_FILE}" ]] || die "Missing template: ${ENV_EXAMPLE_FILE}"
create_env_if_missing
set -a
source "${ENV_FILE}"
set +a
: "${GITEA_BASE_PATH:?GITEA_BASE_PATH is required in .env}"
: "${GITEA_HTTP_PORT:?GITEA_HTTP_PORT is required in .env}"
: "${GITEA_SSH_PORT:?GITEA_SSH_PORT is required in .env}"
: "${GITEA_DOMAIN:?GITEA_DOMAIN is required in .env}"
}
compose() {
docker compose --env-file "${ENV_FILE}" -f "${COMPOSE_FILE}" "$@"
}
wait_for_gitea_health() {
local retries=45
local delay=4
local url="http://localhost:${GITEA_HTTP_PORT}/api/healthz"
log "Waiting for Gitea health endpoint: ${url}"
for ((i=1; i<=retries; i++)); do
if curl -fsS "${url}" >/dev/null 2>&1; then
log "Gitea health check passed"
return 0
fi
sleep "${delay}"
done
die "Gitea did not become healthy in time"
}
ufw_active() {
command -v ufw >/dev/null 2>&1 || return 1
sudo ufw status | grep -q '^Status: active'
}
apply_firewall_rules() {
local open_public_web="${1:-false}"
if ufw_active; then
log "Applying UFW rules for Gitea ports"
sudo ufw allow "${GITEA_HTTP_PORT}/tcp" comment 'Gitea HTTP'
sudo ufw allow "${GITEA_SSH_PORT}/tcp" comment 'Gitea SSH'
if [[ "${open_public_web}" == "true" ]]; then
log "Applying UFW rules for public reverse proxy ports"
sudo ufw allow 80/tcp comment 'Gitea TLS HTTP-01'
sudo ufw allow 443/tcp comment 'Gitea TLS HTTPS'
fi
else
log "UFW not active; skipping firewall rule setup"
fi
}
remove_firewall_rules() {
local close_public_web="${1:-false}"
if ufw_active; then
log "Removing UFW rules for Gitea ports"
sudo ufw --force delete allow "${GITEA_HTTP_PORT}/tcp" || true
sudo ufw --force delete allow "${GITEA_SSH_PORT}/tcp" || true
if [[ "${close_public_web}" == "true" ]]; then
log "Removing UFW rules for public reverse proxy ports"
sudo ufw --force delete allow 80/tcp || true
sudo ufw --force delete allow 443/tcp || true
fi
else
log "UFW not active; skipping firewall rule removal"
fi
}

72
scripts/test.sh Executable file
View File

@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/lib.sh"
WITH_TLS=false
ALLOW_PENDING_EXTERNAL=false
for arg in "$@"; do
case "$arg" in
--with-tls) WITH_TLS=true ;;
--allow-pending-external) ALLOW_PENDING_EXTERNAL=true ;;
*) die "Unknown argument: $arg" ;;
esac
done
load_env
require_cmd curl
log "Checking docker compose status"
if [[ "${WITH_TLS}" == "true" ]]; then
compose --profile tls ps
else
compose ps
fi
log "Checking local HTTP health"
curl -fsS "http://localhost:${GITEA_HTTP_PORT}/api/healthz" && printf '\n'
log "Checking local web root"
curl -I -sS "http://localhost:${GITEA_HTTP_PORT}" | grep -E 'HTTP/[0-9.]+ 200|HTTP/[0-9.]+ 302'
if command -v getent >/dev/null 2>&1; then
log "Checking DNS resolution for ${GITEA_DOMAIN}"
getent hosts "${GITEA_DOMAIN}" || log "DNS resolution not yet set for ${GITEA_DOMAIN}"
fi
if command -v nc >/dev/null 2>&1; then
log "Checking SSH TCP port on localhost"
nc -z localhost "${GITEA_SSH_PORT}"
fi
if [[ "${WITH_TLS}" == "true" ]]; then
log "Checking Caddy container status"
compose --profile tls ps caddy | grep -E 'gitea-caddy|caddy'
if command -v nc >/dev/null 2>&1; then
log "Checking reverse proxy listener ports"
nc -z localhost 80
nc -z localhost 443
fi
if getent hosts "${GITEA_DOMAIN}" >/dev/null 2>&1; then
log "Checking HTTPS response by domain"
if [[ "${ALLOW_PENDING_EXTERNAL}" == "true" ]]; then
if ! curl -k -I -sS "https://${GITEA_DOMAIN}" | grep -E 'HTTP/[0-9.]+ 200|HTTP/[0-9.]+ 302|HTTP/[0-9.]+ 308'; then
log "HTTPS domain check did not pass yet (pending DNS/routing/certificate propagation)"
fi
else
curl -I -sS "https://${GITEA_DOMAIN}" | grep -E 'HTTP/[0-9.]+ 200|HTTP/[0-9.]+ 302|HTTP/[0-9.]+ 308'
fi
else
if [[ "${ALLOW_PENDING_EXTERNAL}" == "true" ]]; then
log "Skipping strict HTTPS domain check until DNS is configured"
else
die "DNS not configured for ${GITEA_DOMAIN}; rerun with --allow-pending-external if still propagating"
fi
fi
fi
log "All tests passed"

50
scripts/uninstall.sh Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/lib.sh"
PURGE_DATA=false
PURGE_IMAGES=false
ASSUME_YES=false
WITH_TLS=false
CLOSE_PUBLIC_WEB=false
for arg in "$@"; do
case "$arg" in
--purge-data) PURGE_DATA=true ;;
--purge-images) PURGE_IMAGES=true ;;
--yes) ASSUME_YES=true ;;
--with-tls) WITH_TLS=true ;;
--close-public-web) CLOSE_PUBLIC_WEB=true ;;
*) die "Unknown argument: $arg" ;;
esac
done
load_env
if [[ "${PURGE_DATA}" == "true" && "${ASSUME_YES}" != "true" ]]; then
read -r -p "This will delete ${GITEA_BASE_PATH}/gitea-data and ${GITEA_BASE_PATH}/postgres. Continue? [y/N]: " answer
[[ "${answer}" == "y" || "${answer}" == "Y" ]] || die "Aborted by user"
fi
log "Stopping and removing containers"
if [[ "${WITH_TLS}" == "true" ]]; then
compose --profile tls down --remove-orphans
else
compose down --remove-orphans
fi
if [[ "${PURGE_IMAGES}" == "true" ]]; then
log "Removing docker images"
docker image rm gitea/gitea:1.24.2 postgres:16-alpine caddy:2.10-alpine || true
fi
if [[ "${PURGE_DATA}" == "true" ]]; then
log "Purging persistent data under ${GITEA_BASE_PATH}"
rm -rf "${GITEA_BASE_PATH}/gitea-data" "${GITEA_BASE_PATH}/postgres" "${GITEA_BASE_PATH}/caddy-data" "${GITEA_BASE_PATH}/caddy-config"
fi
remove_firewall_rules "${CLOSE_PUBLIC_WEB}"
log "Uninstall complete"