#!/bin/bash # ============================================================================= # FORGEJO RUNNER - FULL SYSTEM DEPLOYMENT WITH SYSTEMD # Supports both DIRECT and DOCKER modes # ============================================================================= # --- CONFIGURATION VARIABLES --- FORGEJO_URL="http://192.168.2.76:3006" # CHANGE THIS REGISTRATION_TOKEN="your-registration-token" # CHANGE THIS RUNNER_NAME="selfhosted-runner-01" RUNNER_VERSION="latest" # "latest" or specific version RUNNER_MODE="direct" # "direct" or "docker" # Direct mode variables RUNNER_USER="forgejo-runner" RUNNER_HOME="/opt/forgejo-runner" # Docker mode variables COMPOSE_DIR="$HOME/forgejo-runner" DIND_IMAGE="docker:dind" RUNNER_IMAGE="data.forgejo.org/forgejo/runner" # ============================================================================= set -e echo "============================================================" echo "FORGEJO RUNNER DEPLOYMENT" echo "============================================================" echo "Mode: $RUNNER_MODE" echo "Instance: $FORGEJO_URL" echo "" # --- STEP 1: Detect architecture --- echo "Detecting architecture..." export ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/;s/armv7l/armv7/') echo "Architecture: $ARCH" # --- STEP 2: Get latest version --- if [ "$RUNNER_VERSION" = "latest" ]; then echo "Fetching latest version..." export RUNNER_VERSION=$(curl -s https://code.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | grep -o '"tag_name":"v[0-9]\+\.[0-9]\+\.[0-9]\+"' | head -1 | sed 's/"tag_name":"//' | sed 's/"//' | sed 's/^v//') echo "Version: $RUNNER_VERSION" else RUNNER_VERSION=${RUNNER_VERSION#v} echo "Version: $RUNNER_VERSION" fi echo "" # ============================================================================= # DIRECT MODE # ============================================================================= if [ "$RUNNER_MODE" = "direct" ]; then echo "=== DIRECT MODE INSTALLATION ===" echo "Installing runner directly on the system" echo "" # --- Create runner user if not exists --- echo "Creating runner user..." if ! id "$RUNNER_USER" &>/dev/null; then sudo useradd -r -m -d "$RUNNER_HOME" -s /bin/bash "$RUNNER_USER" echo "User $RUNNER_USER created" else echo "User $RUNNER_USER already exists" fi # --- Create directory structure --- echo "Creating directory structure..." sudo mkdir -p "$RUNNER_HOME/bin" sudo chown -R "$RUNNER_USER":"$RUNNER_USER" "$RUNNER_HOME" # --- Download binary --- echo "Downloading runner binary..." sudo -u "$RUNNER_USER" wget -O "$RUNNER_HOME/bin/forgejo-runner" \ https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-${ARCH} # --- Make executable --- echo "Making executable..." sudo chmod +x "$RUNNER_HOME/bin/forgejo-runner" # --- Register runner --- echo "Registering runner with Forgejo..." sudo -u "$RUNNER_USER" "$RUNNER_HOME/bin/forgejo-runner" register \ --token "$REGISTRATION_TOKEN" \ --name "$RUNNER_NAME" \ --instance "$FORGEJO_URL" \ --labels "$RUNNER_NAME:host" \ --no-interactive # --- Create config.yaml --- echo "Creating configuration..." sudo -u "$RUNNER_USER" tee "$RUNNER_HOME/config.yaml" > /dev/null << EOF instance: $FORGEJO_URL token: "$REGISTRATION_TOKEN" log: level: info runner: file: .runner capacity: 1 timeout: 30m shutdown_timeout: 30m insecure: true fetch_timeout: 30s fetch_interval: 60s report_interval: 10s labels: - "$RUNNER_NAME:host" cache: enabled: true dir: "" host: workdir_parent: "" EOF # --- Create systemd service --- echo "Creating systemd service..." sudo tee /etc/systemd/system/forgejo-runner.service > /dev/null << EOF [Unit] Description=Forgejo Actions Runner After=network.target [Service] Type=simple User=$RUNNER_USER Group=$RUNNER_USER WorkingDirectory=$RUNNER_HOME ExecStart=$RUNNER_HOME/bin/forgejo-runner daemon --config $RUNNER_HOME/config.yaml Restart=always RestartSec=10 StartLimitBurst=3 StartLimitInterval=60s LimitNOFILE=65536 LimitNPROC=65536 [Install] WantedBy=multi-user.target EOF # --- Enable and start service --- echo "Starting service..." sudo systemctl daemon-reload sudo systemctl enable forgejo-runner.service sudo systemctl start forgejo-runner.service echo "" echo "=== DIRECT MODE DEPLOYMENT COMPLETE ===" echo "" echo "Service status:" sudo systemctl status forgejo-runner.service --no-pager echo "" echo "To view logs:" echo " sudo journalctl -u forgejo-runner -f" echo "" echo "Runner location: $RUNNER_HOME/bin/forgejo-runner" echo "Config location: $RUNNER_HOME/config.yaml" echo "User: $RUNNER_USER" # ============================================================================= # DOCKER MODE # ============================================================================= elif [ "$RUNNER_MODE" = "docker" ]; then echo "=== DOCKER MODE INSTALLATION ===" echo "Installing runner with Docker Compose" echo "" # --- Check Docker --- echo "Checking Docker..." if ! command -v docker &> /dev/null; then echo "Docker not found. Installing Docker..." curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER sudo systemctl enable docker --now rm get-docker.sh echo "Docker installed successfully" else echo "Docker is already installed" fi if ! systemctl is-active --quiet docker; then echo "Starting Docker service..." sudo systemctl start docker fi echo "Docker is running" # --- Check Docker Compose --- echo "Checking Docker Compose..." if ! command -v docker compose &> /dev/null; then echo "Docker Compose not found. Installing..." if command -v apt-get &> /dev/null; then sudo apt-get update && sudo apt-get install -y docker-compose-plugin elif command -v yum &> /dev/null; then sudo yum install -y docker-compose-plugin else echo "ERROR: Could not install docker-compose-plugin" echo "Please install Docker Compose manually" exit 1 fi else echo "Docker Compose is already installed" fi echo "" # --- Create working directory --- echo "Creating working directory..." mkdir -p "$COMPOSE_DIR"/{data,cache} cd "$COMPOSE_DIR" # --- Register runner using Docker --- echo "Registering runner with Forgejo..." docker run -it --rm \ -v $(pwd)/data:/data \ -w /data \ --user root \ ${RUNNER_IMAGE}:${RUNNER_VERSION} \ forgejo-runner register \ --no-interactive \ --token "$REGISTRATION_TOKEN" \ --name "$RUNNER_NAME" \ --instance "$FORGEJO_URL" \ --labels "$RUNNER_NAME:host" # --- Generate config.yaml --- echo "Creating configuration..." cat > data/config.yaml << EOF instance: $FORGEJO_URL token: "$REGISTRATION_TOKEN" log: level: info runner: file: .runner capacity: 1 timeout: 30m shutdown_timeout: 30m insecure: true fetch_timeout: 30s fetch_interval: 60s report_interval: 10s labels: - "$RUNNER_NAME:host" cache: enabled: true dir: "" container: privileged: false valid_volumes: - '**' docker_host: "" force_pull: false host: workdir_parent: "" EOF # --- Create docker-compose.yml --- echo "Creating docker-compose.yml..." cat > docker-compose.yml << EOF version: '3.8' services: docker-in-docker: image: ${DIND_IMAGE} container_name: ${RUNNER_NAME}_dind privileged: true command: ['dockerd', '-H', 'tcp://0.0.0.0:2375', '--tls=false'] restart: unless-stopped volumes: - dind-storage:/var/lib/docker runner: image: ${RUNNER_IMAGE}:${RUNNER_VERSION} container_name: ${RUNNER_NAME} depends_on: docker-in-docker: condition: service_started environment: DOCKER_HOST: tcp://docker-in-docker:2375 user: 0:0 volumes: - ./data:/data - ./cache:/cache restart: unless-stopped command: /bin/sh -c " apk update && apk add --no-cache nodejs npm && sleep 5 && forgejo-runner daemon --config /data/config.yaml " volumes: dind-storage: EOF # --- Create systemd service --- echo "Creating systemd service..." sudo tee /etc/systemd/system/forgejo-runner-docker.service > /dev/null << EOF [Unit] Description=Forgejo Runner (Docker Compose) After=docker.service Requires=docker.service [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=$COMPOSE_DIR ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down ExecReload=/usr/bin/docker compose restart StandardOutput=journal User=$USER Group=$USER [Install] WantedBy=multi-user.target EOF # --- Enable and start service --- echo "Starting service..." sudo systemctl daemon-reload sudo systemctl enable forgejo-runner-docker.service sudo systemctl start forgejo-runner-docker.service echo "" echo "=== DOCKER MODE DEPLOYMENT COMPLETE ===" echo "" echo "Service status:" sudo systemctl status forgejo-runner-docker.service --no-pager echo "" echo "To view logs:" echo " docker compose -f $COMPOSE_DIR/docker-compose.yml logs -f runner" echo "" echo "To manage:" echo " cd $COMPOSE_DIR" echo " docker compose up -d # start" echo " docker compose down # stop" echo " docker compose restart # restart" echo "" echo "Runner location: $COMPOSE_DIR" # ============================================================================= # INVALID MODE # ============================================================================= else echo "ERROR: Invalid RUNNER_MODE. Use 'direct' or 'docker'" exit 1 fi echo "" echo "============================================================" echo "DEPLOYMENT COMPLETE" echo "============================================================" echo "" echo "Runner should now appear online in Forgejo Admin -> Actions -> Runners" echo "URL: $FORGEJO_URL/admin/actions/runners" echo "" if [ "$RUNNER_MODE" = "direct" ]; then echo "To check runner logs: sudo journalctl -u forgejo-runner -f" echo "Runner user: $RUNNER_USER" echo "Runner home: $RUNNER_HOME" else echo "To check runner logs: docker compose -f $COMPOSE_DIR/docker-compose.yml logs -f runner" echo "Runner location: $COMPOSE_DIR" fi echo "============================================================"