Files
docker/git/Gemini.md

13 KiB

Okay, let's switch gears to run Gitea using Docker and Docker Compose. This is generally the recommended approach as it isolates Gitea and its dependencies.

We'll still create a user on the host system to own the data volumes, ensuring correct permissions outside the container. The Gitea container itself runs internally as a user (usually git, UID/GID 1000 by default in the official image), and Docker maps the volume permissions.

Key Components:

  1. docker-compose.yml: Defines the Gitea service and potentially a database service (e.g., PostgreSQL).
  2. Host Directory Structure: Directories on the host machine to store persistent Gitea data (repos, config, database files, logs).
  3. Host User (git): A user on the host system to own these directories, matching the UID/GID used inside the container.
  4. app.ini: The Gitea configuration file, placed in the host directory structure to be mounted into the container.

Steps:

1. Create Host User and Group (if they don't exist)

We need a user and group on the host machine. We'll use the standard non-privileged UID/GID 1000, which the official Gitea Docker image typically uses internally for its git user.

# Run these commands with sudo
sudo groupadd --gid 1000 git || echo "Group git (GID 1000) likely already exists."
sudo useradd --system --uid 1010 --gid 1010 --comment "Gitea Docker User" --shell /usr/sbin/nologin --home /opt/gitea git || echo "User git (UID 1010) likely already exists."
# Verify (optional)
id git
  • If UID/GID 1000 is already taken on your system, choose another (e.g., 1001) and make sure to specify it in the docker-compose.yml using user: UID:GID. However, sticking to 1000 is often simplest if possible.
  • --shell /usr/sbin/nologin prevents this user from being used for interactive logins.
  • --home /opt/gitea sets a nominal home directory, though we won't rely on it directly.

2. Create Host Directory Structure and Initial app.ini

We'll create a directory (e.g., /opt/gitea) on the host to hold all persistent data and configuration.

#!/bin/bash

# Exit immediately if a command exits with a non-zero status.
set -e

# --- Configuration ---
HOST_GITEA_ROOT="/opt/gitea" # Or choose another location like /srv/gitea
HOST_GITEA_DATA_DIR="${HOST_GITEA_ROOT}/data"
HOST_GITEA_CONFIG_DIR="${HOST_GITEA_ROOT}/config" # Gitea image expects config in /data/gitea/conf
HOST_GITEA_LOG_DIR="${HOST_GITEA_ROOT}/log"     # We'll mount this separately for clarity
HOST_USER_UID=1010 # Must match the UID used inside the container (or specified in docker-compose)
HOST_USER_GID=1010 # Must match the GID used inside the container (or specified in docker-compose)

# --- Pre-checks ---
if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root (or using sudo)"
   exit 1
fi
if ! getent passwd "${HOST_USER_UID}" > /dev/null 2>&1; then
    echo "ERROR: User with UID ${HOST_USER_UID} does not exist. Please create it first."
    exit 1
fi
if ! getent group "${HOST_USER_GID}" > /dev/null 2>&1; then
    echo "ERROR: Group with GID ${HOST_USER_GID} does not exist. Please create it first."
    exit 1
fi


echo "--- Gitea Docker Host Setup ---"

# --- Create Directories ---
echo "[1/3] Creating host directories in ${HOST_GITEA_ROOT}..."
mkdir -pv "${HOST_GITEA_DATA_DIR}"
mkdir -pv "${HOST_GITEA_CONFIG_DIR}"
mkdir -pv "${HOST_GITEA_LOG_DIR}"
echo "Host directories created."

# --- Create Minimal app.ini ---
# Note: Gitea container expects app.ini at /data/gitea/conf/app.ini
# We will mount HOST_GITEA_CONFIG_DIR to /etc/gitea for easier access,
# but Gitea might ignore it if /data/gitea/conf/app.ini exists from the volume mount.
# A safer approach is often to let Gitea create the first config inside the /data mount
# and then modify it, or mount the specific file. Let's mount the dir /etc/gitea for now.

GITEA_CONF_FILE="${HOST_GITEA_CONFIG_DIR}/app.ini"
echo "[2/3] Creating minimal configuration file: ${GITEA_CONF_FILE}..."

cat << EOF > "${GITEA_CONF_FILE}"
; Example Gitea Configuration File for Docker
; Paths here usually refer to locations INSIDE the container.
; For details, see: https://docs.gitea.com/administration/config-cheat-sheet

APP_NAME = Gitea Docker
RUN_USER = git  ; User inside the container
RUN_MODE = prod

[server]
DOMAIN           = git.ptrwd.com  ; CHANGE THIS to your actual domain/IP
HTTP_PORT        = 3000           ; Port inside the container
ROOT_URL         = http://git.ptrwd.com:3000/ ; CHANGE THIS along with DOMAIN
DISABLE_SSH      = false
SSH_DOMAIN       = %(DOMAIN)s     ; Use value from DOMAIN
SSH_PORT         = 22             ; Port inside the container (we map 2222 to this externally)
SSH_LISTEN_PORT  = 22             ; Make built-in SSH listen on port 22 inside the container
START_SSH_SERVER = true           ; Use the built-in SSH server
LFS_START_SERVER = true
LFS_CONTENT_PATH = /data/gitea/lfs ; Path inside the container

[repository]
ROOT = /data/git/gitea-repositories ; Path inside the container

[log]
ROOT_PATH = /data/log         ; Path inside container (maps to HOST_GITEA_LOG_DIR)
MODE      = console,file      ; Log to console (for docker logs) and file
LEVEL     = Info

[database]
; === IMPORTANT: Configure your database ===
; Option 1: SQLite3 (Simplest, place DB inside the data volume)
DB_TYPE  = sqlite3
PATH     = /data/gitea/gitea.db  ; Path inside the container
;
; Option 2: PostgreSQL (Recommended for production, use docker-compose service name as host)
; DB_TYPE  = postgres
; HOST     = db:5432             ; 'db' is the service name in docker-compose.yml
; NAME     = gitea
; USER     = gitea
; PASSWD   = 'YOUR_POSTGRES_PASSWORD' ; Use secrets or env vars in real setup
; SSL_MODE = disable
;
; Option 3: MySQL
; DB_TYPE = mysql
; HOST    = db:3306              ; 'db' is the service name in docker-compose.yml
; NAME    = gitea
; USER    = gitea
; PASSWD  = 'YOUR_MYSQL_PASSWORD' ; Use secrets or env vars in real setup
; CHARSET = utf8mb4

[service]
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL     = false
DISABLE_REGISTRATION   = false
ENABLE_CAPTCHA         = false
REQUIRE_SIGNIN_VIEW    = false

[security]
INSTALL_LOCK = true ; Set to true AFTER first install via web UI
SECRET_KEY   = PLEASE_CHANGE_ME_SECRET_KEY_! ; CHANGE THIS! Generate with 'docker exec <container_name> gitea generate secret SECRET_KEY'
INTERNAL_TOKEN = PLEASE_CHANGE_ME_INTERNAL_TOKEN_! ; CHANGE THIS! Generate with 'docker exec <container_name> gitea generate secret INTERNAL_TOKEN'
PASSWORD_HASH_ALGO = pbkdf2 ; Default and recommended

[session]
PROVIDER = file
PROVIDER_CONFIG = /data/sessions ; Path inside the container

[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars ; Path inside the container
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars ; Path inside the container

[attachment]
PATH = /data/gitea/attachments ; Path inside the container

EOF
echo "Minimal ${GITEA_CONF_FILE} created."
echo " >> IMPORTANT: Review and edit ${GITEA_CONF_FILE} with your specific settings (DOMAIN, ROOT_URL, Database, Secret Keys)!"

# --- Set Ownership ---
echo "[3/3] Setting ownership for ${HOST_GITEA_ROOT} to ${HOST_USER_UID}:${HOST_USER_GID}..."
chown -R "${HOST_USER_UID}":"${HOST_USER_GID}" "${HOST_GITEA_ROOT}"
# Set permissions (optional, ownership is key for docker volumes)
# chmod -R u+rwX,go= "${HOST_GITEA_ROOT}" # Give owner full control, deny group/others
chmod 750 "${HOST_GITEA_ROOT}" # Owner rwx, group rx, others ---
find "${HOST_GITEA_ROOT}" -type d -exec chmod 750 {} \; # Dirs: owner rwx, group rx
find "${HOST_GITEA_ROOT}" -type f -exec chmod 640 {} \; # Files: owner rw, group r
echo "Ownership and basic permissions set."

echo "--- Host Setup Complete ---"
echo "Next steps:"
echo "1. **CRITICAL:** Edit ${GITEA_CONF_FILE} for your specific needs (Domain, Database, Secrets!)."
echo "2. Create the docker-compose.yml file."
echo "3. Run 'docker compose up -d' in the directory containing docker-compose.yml."

How to use the script:

  1. Save the script (e.g., setup_docker_gitea_host.sh).
  2. Make it executable: chmod +x setup_docker_gitea_host.sh.
  3. Run with sudo: sudo ./setup_docker_gitea_host.sh.
  4. Crucially, edit the generated /opt/gitea/config/app.ini file with your actual domain, database choice, and generate/set real secret keys before starting the container.

3. Create docker-compose.yml

Create this file in a convenient location (e.g., in /opt/gitea or a dedicated project directory). This example uses PostgreSQL, which is recommended for production.

# /opt/gitea/docker-compose.yml
# or ~/gitea-docker/docker-compose.yml

networks:
  gitea-net:
    external: false

services:
  gitea:
    image: gitea/gitea:latest # Use a specific version tag in production, e.g., :1.21.5
    container_name: gitea
    restart: always
    environment:
      # All config is primarily done via app.ini now, but you could pass some simpler ones here
      - USER_UID=1010 # Matches the host user created
      - USER_GID=1010 # Matches the host group created
      - GITEA__database__DB_TYPE=postgres  # Example: Overriding app.ini via env var if needed
      - GITEA__database__HOST=db:5432      # Using double underscore convention
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea_password # CHANGE THIS! Use docker secrets ideally
    networks:
      - gitea-net
    volumes:
      # Mount the main data directory from the host
      - ./data:/data # Maps host's ./data (relative to compose file) to container's /data
      # Mount the config directory separately (optional, but explicit)
      - ./config:/etc/gitea # Maps host's ./config to container's /etc/gitea
      # Mount log directory
      - ./log:/data/log # Maps host's ./log to container's /data/log (matches app.ini)
      # Timezone consistency
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000" # Map host port 3000 to container port 3000 (HTTP)
      - "2222:22"   # Map host port 2222 to container port 22 (SSH)
    depends_on:
      db:
        condition: service_healthy # Wait for postgres to be healthy

  db:
    image: postgres:15 # Use a specific version
    container_name: gitea-db
    restart: always
    environment:
      - POSTGRES_USER=gitea         # CHANGE THIS if needed
      - POSTGRES_PASSWORD=gitea_password # CHANGE THIS! Must match GITEA__database__PASSWD
      - POSTGRES_DB=gitea           # CHANGE THIS if needed
    networks:
      - gitea-net
    volumes:
      # Persist postgres data on the host - create a 'postgres' dir next to 'data', 'config', 'log'
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      # Basic check to see if postgres is ready to accept connections
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
      interval: 10s
      timeout: 5s
      retries: 5

 volumes:
   gitea_data:
   gitea_config:
   postgres_data:

Explanation:

  • user: "1000:1000" (Optional but good practice): You can explicitly add this under the gitea: service definition to force the container process to run as UID/GID 1000, ensuring it matches the host directory ownership. The official image usually handles this, but being explicit can prevent permission issues.
  • Volumes (./data:/data, etc.): We use bind mounts. This maps the directories we created on the host (/opt/gitea/data, /opt/gitea/config, etc.) directly into the specified paths inside the container. Docker uses the ownership we set on the host directories (chown 1000:1000).
  • app.ini Location: The Gitea image typically looks for app.ini within its data directory (/data/gitea/conf/app.ini). By mounting ./config:/etc/gitea and ./data:/data, the container should find the config. If you have issues, you might need to adjust the volume mount for config or ensure the app.ini is placed at /opt/gitea/data/gitea/conf/app.ini on the host before starting. Mounting /etc/gitea is often cleaner if the image respects GITEA_CUSTOM. For simplicity, the setup script places it in ./config, matching the volume mount. Adjust if Gitea doesn't pick it up.
  • Database: The db service uses the official Postgres image. Its data is persisted in ./postgres on the host. The Gitea container connects to it using the service name db as the hostname. Change the default passwords!
  • Ports: 3000:3000 maps the web UI port. 2222:22 maps host port 2222 to the container's SSH port 22 (make sure your host's firewall allows port 2222).
  • Networks: A dedicated network gitea-net allows the containers to communicate easily using service names (gitea, db).
  • Healthcheck: Ensures Gitea only starts after PostgreSQL is ready.

4. Running Gitea

  1. Navigate to the directory where you saved docker-compose.yml (e.g., /opt/gitea).
  2. Ensure you have edited /opt/gitea/config/app.ini appropriately (Domain, DB, Secrets). Generate secrets using docker run --rm gitea/gitea:latest gitea generate secret SECRET_KEY etc., if needed.
  3. Start the services in detached mode:
    cd /opt/gitea
    docker compose up -d
    
  4. Check the logs:
    docker compose logs -f gitea
    docker compose logs -f db
    
  5. Access Gitea in your browser at http://<your-host-ip-or-domain>:3000.
  6. Clone via SSH using ssh://git@<your-host-ip-or-domain>:2222/user/repo.git.

This Docker setup provides isolation, easier dependency management (database), and uses host volumes with correct permissions managed by the git (UID/GID 1000) user on the host.