commit f6ba9ab02db58d2df907af23d2d6573eb761f4d9 Author: Saad Ibn-Ezzoubayr Date: Fri Jun 19 15:35:48 2026 +0100 chore: bootstrap Atay Makhzan ops repo diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..fac3e1b --- /dev/null +++ b/.env.example @@ -0,0 +1,22 @@ +# Copy to .env on the VPS and fill real values there. +# Never commit the real .env file. + +GITEA_VERSION=1.26.2 +POSTGRES_VERSION=16-alpine + +GITEA_DOMAIN=ataymakhzan.com +GITEA_SSH_DOMAIN=ataymakhzan.com +GITEA_ROOT_URL=https://ataymakhzan.com/ +GITEA_HTTP_PORT=3001 +GITEA_HTTP_BIND=127.0.0.1 +GITEA_SSH_PORT=2222 + +GITEA_USER_UID=1000 +GITEA_USER_GID=1000 + +POSTGRES_DB=gitea +POSTGRES_USER=gitea +POSTGRES_PASSWORD=change-me-before-deploy + +GITEA_CONTAINER_NAME=gitea +POSTGRES_CONTAINER_NAME=gitea-db diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..499fb55 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# Secrets +.env +.env.* +!.env.example +app.ini +*.pem +*.key +*.crt +id_rsa* +id_ed25519* +*_token* +*secret* + +# Backups and dumps +backups/ +*.dump +*.sql +*.sqlite +*.zip +*.tar +*.tar.gz +*.tgz + +# Runtime data +gitea-data/ +postgres-data/ +log/ +logs/ + +# Local/editor +.DS_Store +.idea/ +.vscode/ +*.swp diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5d1db7d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Saad ibn Zoubayr + +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..54353d1 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Atay Makhzan Ops + +Open-source operations repository for **Atay Makhzan**, Saad ibn Zoubayr's self-hosted Gitea forge. + +Atay Makhzan is currently a sovereign Git forge running Gitea on a VPS with Docker Compose, PostgreSQL, Nginx, and SSH Git access. + +## Current production snapshot + +| Area | Current value | +|---|---| +| Public domain | `ataymakhzan.com` | +| Forge | Gitea | +| Gitea image | `gitea/gitea:1.26.2` | +| Database | PostgreSQL via `postgres:16-alpine` | +| Stack path | `/opt/gitea` | +| Web proxy | Nginx + Certbot TLS | +| Local Gitea HTTP | `127.0.0.1:3001` / container port `3001` | +| Git SSH | `ataymakhzan.com:2222` | + +## What belongs in this repo + +- Sanitized Docker Compose templates +- Nginx reverse-proxy templates +- Backup, verification, and upgrade scripts +- Restore and maintenance runbooks +- Architecture decision records +- Public roadmap for future Atay Makhzan evolution + +## What must never be committed + +- `.env` with real secrets +- Gitea `app.ini` with secrets +- PostgreSQL passwords +- SSH private keys +- Gitea dumps or database dumps +- Repository backups +- API tokens or access tokens +- TLS private keys + +See [`SECURITY.md`](SECURITY.md). + +## Quick commands + +Verify a live instance: + +```bash +DOMAIN=ataymakhzan.com \ +SSH_PORT=2222 \ +OWNER=ibnezzoubayr \ +PROBE_REPO=Empire-OS \ +./scripts/verify-gitea.sh +``` + +Create a rollback backup on the VPS: + +```bash +sudo STACK_DIR=/opt/gitea ./scripts/backup-gitea.sh +``` + +Prepare an upgrade dry-run: + +```bash +sudo TARGET_VERSION=1.26.2 STACK_DIR=/opt/gitea ./scripts/upgrade-gitea.sh +``` + +Apply an upgrade intentionally: + +```bash +sudo TARGET_VERSION=1.26.2 STACK_DIR=/opt/gitea APPLY=1 ./scripts/upgrade-gitea.sh +``` + +## Strategic direction + +This repo starts as **ops/infrastructure** for the official Gitea-based Atay Makhzan deployment. + +Later, if Atay Makhzan needs product behavior that Gitea cannot cleanly support through configuration, themes, plugins, or external automation, we can create a separate source fork and maintain it as its own product. + +Until then, the CTO rule is: + +> Do not fork Gitea prematurely. First make the deployment reproducible, observable, backed up, and safe to upgrade. + +See [`docs/FUTURE-GITEA-FORK.md`](docs/FUTURE-GITEA-FORK.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..2ce64be --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,37 @@ +# Security Policy + +This repository is public. Treat it as documentation and reproducible operations code, not as a secret store. + +## Never commit + +- Real `.env` files +- Gitea `app.ini` secrets +- Database passwords +- SMTP credentials +- OAuth secrets +- SSH private keys +- API tokens +- Gitea dump archives +- PostgreSQL dump files +- TLS private keys +- Full production logs containing sensitive data + +## Safe to commit + +- `.env.example` with placeholders +- Docker Compose templates with variable references +- Nginx templates without private keys +- Runbooks +- Scripts that read secrets from the environment +- Architecture records + +## If a secret is committed + +1. Rotate the secret immediately. +2. Remove it from current files. +3. Rewrite public Git history only if the exposure is severe and worth the operational risk. +4. Assume the secret was copied by someone else. + +## Operational rule + +For production maintenance, create backups before upgrades and verify both web and SSH Git paths after changes. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..de4ff7f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +services: + server: + image: gitea/gitea:${GITEA_VERSION:-1.26.2} + container_name: ${GITEA_CONTAINER_NAME:-gitea} + restart: unless-stopped + environment: + - USER_UID=${GITEA_USER_UID:-1000} + - USER_GID=${GITEA_USER_GID:-1000} + - GITEA__database__DB_TYPE=postgres + - GITEA__database__HOST=db:5432 + - GITEA__database__NAME=${POSTGRES_DB:-gitea} + - GITEA__database__USER=${POSTGRES_USER:-gitea} + - GITEA__database__PASSWD=${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} + - GITEA__server__DOMAIN=${GITEA_DOMAIN:-ataymakhzan.com} + - GITEA__server__SSH_DOMAIN=${GITEA_SSH_DOMAIN:-ataymakhzan.com} + - GITEA__server__ROOT_URL=${GITEA_ROOT_URL:-https://ataymakhzan.com/} + - GITEA__server__HTTP_PORT=${GITEA_HTTP_PORT:-3001} + - GITEA__server__SSH_PORT=${GITEA_SSH_PORT:-2222} + volumes: + - ./gitea-data:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "${GITEA_HTTP_BIND:-127.0.0.1}:${GITEA_HTTP_PORT:-3001}:${GITEA_HTTP_PORT:-3001}" + - "${GITEA_SSH_PORT:-2222}:22" + depends_on: + - db + + db: + image: postgres:${POSTGRES_VERSION:-16-alpine} + container_name: ${POSTGRES_CONTAINER_NAME:-gitea-db} + restart: unless-stopped + environment: + - POSTGRES_DB=${POSTGRES_DB:-gitea} + - POSTGRES_USER=${POSTGRES_USER:-gitea} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} + volumes: + - ./postgres-data:/var/lib/postgresql/data diff --git a/docs/ADR-0001-current-gitea-docker-compose.md b/docs/ADR-0001-current-gitea-docker-compose.md new file mode 100644 index 0000000..fdd4e65 --- /dev/null +++ b/docs/ADR-0001-current-gitea-docker-compose.md @@ -0,0 +1,49 @@ +# ADR-0001: Current Gitea Docker Compose Architecture + +## Status + +Accepted. + +## Context + +Atay Makhzan needs a sovereign Git forge controlled by Saad ibn Zoubayr. The current operational need is reliability, simple maintenance, backups, and controlled upgrades. + +## Decision + +Run official Gitea in Docker Compose with PostgreSQL, reverse-proxied by Nginx with Certbot TLS. + +Current production shape: + +- Gitea image pinned to `gitea/gitea:1.26.2` +- PostgreSQL image `postgres:16-alpine` +- Gitea HTTP served locally on port `3001` +- Public HTTPS via Nginx on `ataymakhzan.com` +- Git SSH exposed on port `2222` +- Persistent data mounted under `/opt/gitea` + +## Consequences + +### Positive + +- Simple architecture +- Easy backups +- Easy rollback through Docker image pinning and database dumps +- Low operational burden +- Enough for current private forge needs + +### Negative + +- Single VPS is a single point of failure +- Scaling and HA are manual future work +- Public customization is limited unless we theme, extend, or fork +- Production safety depends on disciplined backups and upgrade procedure + +## Future trigger for revisiting + +Revisit this decision if Atay Makhzan needs: + +- Multi-node availability +- Custom product features inside the forge +- Organization-wide policy automation not available in Gitea +- Deep UI/UX changes that themes cannot support +- Integrated CI/package registry workflows beyond Gitea's native capabilities diff --git a/docs/FUTURE-GITEA-FORK.md b/docs/FUTURE-GITEA-FORK.md new file mode 100644 index 0000000..074f0dd --- /dev/null +++ b/docs/FUTURE-GITEA-FORK.md @@ -0,0 +1,60 @@ +# Future Gitea Fork Strategy + +Saad's long-term intent is valid: Atay Makhzan may eventually differ enough from upstream Gitea that we maintain our own source code separately. + +But the sequencing matters. + +## CTO verdict + +Do not fork Gitea just because we can. + +Fork only when one of these is true: + +1. The required feature cannot be achieved with configuration. +2. It cannot be achieved cleanly with Gitea Actions, webhooks, external automation, or themes. +3. The feature is core to Atay Makhzan's identity as a product. +4. Maintaining the patch is cheaper than working around upstream. +5. We have time to track upstream security releases and merge conflicts. + +## Recommended stages + +### Stage 1 — Ops sovereignty + +This repository. + +Goal: make deployment reproducible, documented, backed up, and safe to upgrade. + +### Stage 2 — External extensions + +Use webhooks, scripts, bots, custom templates, and integrations around Gitea without touching upstream source. + +### Stage 3 — Theming and UX identity + +Customize branding, templates, and public surfaces while remaining close to upstream. + +### Stage 4 — Source fork + +Create a dedicated source repository only when Atay Makhzan needs product behavior that upstream Gitea will not support. + +Suggested future repo name: + +```text +ibnezzoubayr/Atay-Makhzan +``` + +This ops repo should remain separate: + +```text +ibnezzoubayr/Atay-Makhzan-Ops +``` + +## Fork discipline + +If we fork: + +- Track upstream Gitea releases. +- Keep a changelog of every divergence. +- Keep custom patches small and isolated. +- Add tests for every custom behavior. +- Maintain an upstream merge schedule. +- Never delay critical upstream security patches for cosmetic customization. diff --git a/docs/RESTORE.md b/docs/RESTORE.md new file mode 100644 index 0000000..7556333 --- /dev/null +++ b/docs/RESTORE.md @@ -0,0 +1,32 @@ +# Restore Notes + +Restoring Atay Makhzan can destroy or overwrite live data. Do not run restore actions without explicit owner approval. + +## Backup artifacts expected + +A complete backup directory should contain: + +- `gitea-dump-.zip` +- `gitea-postgres-.dump` +- `docker-compose.yml` +- `app.ini` +- `metadata.txt` +- `SHA256SUMS` + +## Safer restore principle + +Prefer restoring to a fresh VPS or staging directory first, then verifying repository data before touching production. + +## High-level restore sequence + +1. Provision VPS and install Docker/Nginx. +2. Restore sanitized Compose configuration. +3. Restore PostgreSQL dump into a fresh database. +4. Restore Gitea data from Gitea dump according to the Gitea version documentation. +5. Start containers. +6. Verify API, web, SSH auth, and `git ls-remote` on known repositories. +7. Switch DNS/proxy only after verification. + +## Production warning + +Do not remove `/opt/gitea/gitea-data` or `/opt/gitea/postgres-data` unless a verified backup exists and Saad explicitly approved the restore. diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md new file mode 100644 index 0000000..f4e0008 --- /dev/null +++ b/docs/RUNBOOK.md @@ -0,0 +1,71 @@ +# Atay Makhzan Runbook + +## Deployment shape + +Atay Makhzan currently runs as a Docker Compose stack on a VPS: + +- Stack directory: `/opt/gitea` +- Gitea container: `gitea` +- PostgreSQL container: `gitea-db` +- Public HTTPS: `https://ataymakhzan.com` +- Local Gitea HTTP: `http://127.0.0.1:3001` +- Git SSH: `ssh://git@ataymakhzan.com:2222//.git` + +## Normal health check + +From the VPS: + +```bash +cd /opt/gitea +docker compose ps +curl -fsS http://127.0.0.1:3001/api/v1/version +docker exec -u git gitea gitea doctor check -c /data/gitea/conf/app.ini -w /data/gitea +``` + +From outside: + +```bash +curl -fsS https://ataymakhzan.com/api/v1/version +ssh -p 2222 -o BatchMode=yes -T git@ataymakhzan.com +git ls-remote --heads ssh://git@ataymakhzan.com:2222/ibnezzoubayr/Empire-OS.git +``` + +## Backup before maintenance + +```bash +sudo STACK_DIR=/opt/gitea ./scripts/backup-gitea.sh +``` + +A proper backup should include: + +- Gitea built-in dump +- PostgreSQL `pg_dump -Fc` +- `docker-compose.yml` +- `app.ini` +- metadata and checksums + +## Upgrade policy + +1. Inspect current state. +2. Create backup. +3. Pull target image. +4. Pin explicit Gitea version in Compose. +5. Recreate only the Gitea service. +6. Verify web, API, SSH, `git ls-remote`, and doctor check. + +Do not run production on `gitea/gitea:latest`. + +## Rollback policy + +Rollback can involve code image rollback, config rollback, or database restore. + +- Re-tagged Docker images are low-risk. +- Restoring database dumps is destructive and requires explicit owner approval. +- Never delete volumes during an emergency unless a verified backup exists. + +## Routine cleanup candidates + +- Remove obsolete Compose `version:` key from the live stack. +- Move deprecated Gitea `[picture]` options out of `app.ini` if still present. +- Add backup retention and offsite backup storage. +- Add uptime/health monitoring. diff --git a/nginx/ataymakhzan.conf.template b/nginx/ataymakhzan.conf.template new file mode 100644 index 0000000..12b803d --- /dev/null +++ b/nginx/ataymakhzan.conf.template @@ -0,0 +1,38 @@ +# Nginx reverse proxy template for Atay Makhzan / Gitea. +# Replace DOMAIN and local port values if they differ. + +server { + listen 80; + server_name ataymakhzan.com www.ataymakhzan.com; + + location /.well-known/acme-challenge/ { + root /var/www/html; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl http2; + server_name ataymakhzan.com www.ataymakhzan.com; + + ssl_certificate /etc/letsencrypt/live/ataymakhzan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/ataymakhzan.com/privkey.pem; + + client_max_body_size 512M; + + location / { + proxy_pass http://127.0.0.1:3001; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port 443; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } +} diff --git a/scripts/backup-gitea.sh b/scripts/backup-gitea.sh new file mode 100755 index 0000000..21f4ee7 --- /dev/null +++ b/scripts/backup-gitea.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +STACK_DIR="${STACK_DIR:-/opt/gitea}" +GITEA_CONTAINER="${GITEA_CONTAINER:-gitea}" +DB_CONTAINER="${DB_CONTAINER:-gitea-db}" +POSTGRES_USER="${POSTGRES_USER:-gitea}" +POSTGRES_DB="${POSTGRES_DB:-gitea}" + +cd "$STACK_DIR" +TS="$(date -u +%Y%m%d-%H%M%S)" +BK="$STACK_DIR/backups/$TS" +mkdir -p "$BK" + +cp docker-compose.yml "$BK/docker-compose.yml" +if [ -f "$STACK_DIR/gitea-data/gitea/conf/app.ini" ]; then + cp "$STACK_DIR/gitea-data/gitea/conf/app.ini" "$BK/app.ini" +fi + +{ + echo "backup_utc=$TS" + echo "host=$(hostname)" + echo "date=$(date -u --iso-8601=seconds)" + echo "docker=$(docker --version)" + echo "compose=$(docker compose version 2>/dev/null || true)" + echo "gitea_version=$(docker exec -u git "$GITEA_CONTAINER" gitea --version 2>/dev/null || true)" + docker ps --format '{{.Names}} | {{.Image}} | {{.Status}} | {{.Ports}}' +} > "$BK/metadata.txt" + +docker exec -u git "$GITEA_CONTAINER" mkdir -p /data/gitea/backup-tmp +DUMP_NAME="gitea-dump-$TS.zip" +docker exec -u git "$GITEA_CONTAINER" gitea dump \ + -c /data/gitea/conf/app.ini \ + -w /data/gitea \ + -f "/data/gitea/backup-tmp/$DUMP_NAME" \ + --quiet + +cp "$STACK_DIR/gitea-data/gitea/backup-tmp/$DUMP_NAME" "$BK/$DUMP_NAME" +rm -f "$STACK_DIR/gitea-data/gitea/backup-tmp/$DUMP_NAME" + +docker exec "$DB_CONTAINER" pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc > "$BK/gitea-postgres-$TS.dump" +sha256sum "$BK"/* > "$BK/SHA256SUMS" + +echo "Backup created: $BK" +du -sh "$BK" "$BK"/* diff --git a/scripts/upgrade-gitea.sh b/scripts/upgrade-gitea.sh new file mode 100755 index 0000000..fc0892d --- /dev/null +++ b/scripts/upgrade-gitea.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +set -euo pipefail + +STACK_DIR="${STACK_DIR:-/opt/gitea}" +TARGET_VERSION="${TARGET_VERSION:?set TARGET_VERSION, e.g. TARGET_VERSION=1.26.2}" +GITEA_CONTAINER="${GITEA_CONTAINER:-gitea}" +APPLY="${APPLY:-0}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +cd "$STACK_DIR" + +if [ ! -f docker-compose.yml ]; then + echo "docker-compose.yml not found in $STACK_DIR" >&2 + exit 1 +fi + +echo "== Current version ==" +docker exec -u git "$GITEA_CONTAINER" gitea --version || true + +echo "== Creating backup first ==" +"$SCRIPT_DIR/backup-gitea.sh" + +echo "== Pulling target image ==" +docker pull "gitea/gitea:$TARGET_VERSION" +docker run --rm --user git --entrypoint /usr/local/bin/gitea "gitea/gitea:$TARGET_VERSION" --version || true + +OLD_IMAGE_ID="$(docker image inspect "$(docker inspect --format '{{.Config.Image}}' "$GITEA_CONTAINER")" --format '{{.Id}}' 2>/dev/null || true)" +if [ -n "$OLD_IMAGE_ID" ]; then + ROLLBACK_TAG="gitea/gitea:rollback-$(date -u +%Y%m%d-%H%M%S)" + docker tag "$OLD_IMAGE_ID" "$ROLLBACK_TAG" + echo "Rollback image tag: $ROLLBACK_TAG" +fi + +echo "== Pinning docker-compose.yml to target version ==" +python3 - </dev/null + +if [ "$APPLY" != "1" ]; then + echo "Dry-run complete. Compose file was pinned, but service was not recreated." + echo "Review the diff, then run with APPLY=1 to recreate the Gitea service." + exit 0 +fi + +echo "== Recreating Gitea service only ==" +docker compose up -d server + +echo "== Waiting for readiness ==" +for _ in $(seq 1 90); do + if curl -fsS "http://127.0.0.1:3001/api/v1/version" | grep -q "$TARGET_VERSION"; then + echo + echo "Gitea $TARGET_VERSION is ready." + exit 0 + fi + sleep 2 +done + +echo "Gitea did not report target version in time" >&2 +exit 1 diff --git a/scripts/verify-gitea.sh b/scripts/verify-gitea.sh new file mode 100755 index 0000000..ba1a2dd --- /dev/null +++ b/scripts/verify-gitea.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -euo pipefail + +DOMAIN="${DOMAIN:-ataymakhzan.com}" +SSH_PORT="${SSH_PORT:-2222}" +OWNER="${OWNER:-ibnezzoubayr}" +PROBE_REPO="${PROBE_REPO:-Empire-OS}" +LOCAL_URL="${LOCAL_URL:-http://127.0.0.1:3001}" +GITEA_CONTAINER="${GITEA_CONTAINER:-gitea}" +RUN_DOCKER_CHECKS="${RUN_DOCKER_CHECKS:-1}" + +echo "== Local API version ==" +curl -fsS "$LOCAL_URL/api/v1/version" +echo + +echo "== External API version ==" +curl -fsS "https://$DOMAIN/api/v1/version" +echo + +echo "== External homepage status ==" +curl -fsS -I -L --max-time 20 "https://$DOMAIN/" | sed -n '1,12p' + +echo "== Git SSH authentication ==" +SSH_OUT=$(ssh -p "$SSH_PORT" -o BatchMode=yes -o ConnectTimeout=10 -T "git@$DOMAIN" 2>&1 || true) +echo "$SSH_OUT" +echo "$SSH_OUT" | grep -Eiq 'successfully authenticated|Hi .*!|Welcome' || { + echo "Could not confirm successful SSH authentication from output" >&2 + exit 1 +} + +echo "== Git ls-remote probe ==" +git ls-remote --heads "ssh://git@$DOMAIN:$SSH_PORT/$OWNER/$PROBE_REPO.git" >/dev/null + +echo "== Optional Docker/native checks ==" +if [ "$RUN_DOCKER_CHECKS" = "1" ] && command -v docker >/dev/null 2>&1; then + docker exec -u git "$GITEA_CONTAINER" gitea --version + docker exec -u git "$GITEA_CONTAINER" gitea doctor check -c /data/gitea/conf/app.ini -w /data/gitea +else + echo "Skipping Docker checks. Set RUN_DOCKER_CHECKS=1 on the VPS to enable." +fi + +echo "Atay Makhzan verification passed."