# Dockerfile — GitHub Actions self-hosted runner image. # # Includes: Ubuntu 24.04 + Go 1.26 + Node 24 + GitHub Actions runner agent. # Designed for CI workloads on Linux x64 servers (e.g., Unraid, bare metal). # # Build: # docker build -t augur-runner . # # The image is also auto-built and pushed to GHCR by the # build-runner-image.yml workflow on Dockerfile or entrypoint changes. FROM ubuntu:24.04 # --- Metadata labels (OCI standard) --- LABEL org.opencontainers.image.title="augur-runner" \ org.opencontainers.image.description="GitHub Actions self-hosted runner for augur CI" \ org.opencontainers.image.source="https://github.com/AIinfusedS/augur" \ org.opencontainers.image.licenses="Proprietary" # --- Layer 1: System packages (changes least often) --- # Combined into a single layer to minimize image size. # --no-install-recommends avoids pulling unnecessary packages. RUN apt-get update && \ apt-get install -y --no-install-recommends \ ca-certificates \ curl \ git \ jq \ python3 \ tini \ sudo \ unzip \ && rm -rf /var/lib/apt/lists/* # --- Layer 2: Go 1.26 (pinned version + SHA256 verification) --- ARG GO_VERSION=1.26.0 ARG GO_SHA256=aac1b08a0fb0c4e0a7c1555beb7b59180b05dfc5a3d62e40e9de90cd42f88235 RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" -o /tmp/go.tar.gz && \ echo "${GO_SHA256} /tmp/go.tar.gz" | sha256sum -c - && \ tar -C /usr/local -xzf /tmp/go.tar.gz && \ rm /tmp/go.tar.gz ENV PATH="/usr/local/go/bin:${PATH}" # --- Layer 3: Node 24 LTS via NodeSource --- ARG NODE_MAJOR=24 RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - && \ apt-get install -y --no-install-recommends nodejs && \ rm -rf /var/lib/apt/lists/* # --- Layer 4: Create non-root runner user (UID/GID 1000) --- # Ubuntu 24.04 ships with an 'ubuntu' user at UID/GID 1000. # Remove it first, then create our runner user at the same IDs. RUN userdel -r ubuntu 2>/dev/null || true && \ groupadd -f -g 1000 runner && \ useradd -m -u 1000 -g runner -s /bin/bash runner && \ echo "runner ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/runner # --- Layer 5: GitHub Actions runner agent --- # Downloads the latest runner release for linux-x64. # The runner agent auto-updates itself between jobs, so pinning # the exact version here is not critical. ARG RUNNER_ARCH=x64 RUN RUNNER_VERSION=$(curl -fsSL https://api.github.com/repos/actions/runner/releases/latest \ | jq -r '.tag_name' | sed 's/^v//') && \ curl -fsSL "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-${RUNNER_ARCH}-${RUNNER_VERSION}.tar.gz" \ -o /tmp/runner.tar.gz && \ mkdir -p /home/runner/actions-runner && \ tar -xzf /tmp/runner.tar.gz -C /home/runner/actions-runner && \ rm /tmp/runner.tar.gz && \ chown -R runner:runner /home/runner/actions-runner && \ /home/runner/actions-runner/bin/installdependencies.sh # --- Layer 6: Work directory (pre-create for Docker volume ownership) --- # Docker named volumes inherit ownership from the mount point in the image. # Creating _work as runner:runner ensures the volume is writable without sudo. RUN mkdir -p /home/runner/_work && chown runner:runner /home/runner/_work # --- Layer 7: Entrypoint (changes most often) --- COPY --chown=runner:runner entrypoint.sh /home/runner/entrypoint.sh RUN chmod +x /home/runner/entrypoint.sh # --- Runtime configuration --- USER runner WORKDIR /home/runner/actions-runner # Health check: verify the runner listener process is alive. # start_period gives time for registration + first job pickup. HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=30s \ CMD pgrep -f "Runner.Listener" > /dev/null || exit 1 # Use tini as PID 1 for proper signal forwarding and zombie reaping. ENTRYPOINT ["tini", "--"] CMD ["/home/runner/entrypoint.sh"]