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. ```bash # 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. ```bash #!/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 gitea generate secret SECRET_KEY' INTERNAL_TOKEN = PLEASE_CHANGE_ME_INTERNAL_TOKEN_! ; CHANGE THIS! Generate with 'docker exec 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. ```yaml # /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: ```bash cd /opt/gitea docker compose up -d ``` 4. Check the logs: ```bash docker compose logs -f gitea docker compose logs -f db ``` 5. Access Gitea in your browser at `http://:3000`. 6. Clone via SSH using `ssh://git@: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.