██▒   █▓ ▒█████   ██▓▓█████▄ 
▓██░   █▒▒██▒  ██▒▓██▒▒██▀ ██▌
 ▓██  █▒░▒██░  ██▒▒██▒░██   █▌
  ▒██ █░░▒██   ██░░██░░▓█▄   ▌
   ▒▀█░  ░ ████▓▒░░██░░▒████▓ 
   ░ ▐░  ░ ▒░▒░▒░ ░▓   ▒▒▓  ▒ 
   ░ ░░    ░ ▒ ▒░  ▒ ░ ░ ▒  ▒ 
     ░░  ░ ░ ░ ▒   ▒ ░ ░ ░  ░ 
      ░      ░ ░   ░     ░    
     ░                 ░      
    

 ___      ___ ________  ___  ________     
|\  \    /  /|\   __  \|\  \|\   ___ \    
\ \  \  /  / | \  \|\  \ \  \ \  \_|\ \   
 \ \  \/  / / \ \  \\\  \ \  \ \  \ \\ \  
  \ \    / /   \ \  \\\  \ \  \ \  \_\\ \ 
   \ \__/ /     \ \_______\ \__\ \_______\
    \|__|/       \|_______|\|__|\|_______|
    

Yane ✖ Karov

  • Building a Custom Self-Hosted GitHub Actions Runner with Docker and Upptime

    Learn how to create a Dockerized GitHub Actions runner with this step-by-step guide. Enhance your CI/CD pipeline with a tailored, secure, and efficient runner.

    GitHub Actions CI/CD pipelines offer flexibility and integration with GitHub's ecosystem. I recently found myself in a scenario where I needed a custom self-hosted runner for server up-time monitoring. This guide will show you how to build your own GitHub Actions runner using Docker, providing you with a tailored environment for your workflows with an optional step to integrate with Upptime

    Prerequisites

    • Basic understanding of Docker and GitHub Actions.
    • Docker installed on your system.
    • A GitHub repository to set up the runner.

    Step 1: Creating the Dockerfile

    To create this dockerfile, I hacked together various existing Dockerfile implementations I found on Github to create a runner that would work for my use case. There was a lot of trial and error involved which partly motivated me to write this guide.

    cat << 'EOF' > Dockerfile
    FROM debian:bullseye-slim
    
    # Define arguments for versions and other variables with default values for RUNNER_VERSION
    ARG RUNNER_VERSION=latest
    ARG CHECKSUM
    ARG RUNNER_URL
    ARG RUNNER_TOKEN
    ARG RUNNER_NAME
    ARG LABELS
    ARG RUNNER_GROUP
    ARG DISABLE_UPDATE
    ARG NODE_VERSION=20.8.0
    
    # Fail the build if the CHECKSUM is not provided
    RUN if [ -z "\$CHECKSUM" ]; then echo "CHECKSUM argument not provided" && exit 1; fi
    
    # Install necessary packages (curl, tar, libicu, git, ca-certificates)
    RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    tar \
    libicu67 \
    git \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*
    
    # Create a non-root user and switch to it
    RUN useradd -m runner
    
    # Set up NVM environment variables for the 'runner' user
    ENV NVM_DIR="/home/runner/.nvm"
    ENV npm_config_cache="/home/runner/.npm"
    
    USER runner
    WORKDIR /home/runner
    
    # Install NVM, Node.js, and update npm
    RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash \
    && . $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm use $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && npm install -g npm@latest
    
    # Create actions-runner directory and set permissions
    WORKDIR /actions-runner
    RUN chmod 755 /actions-runner
    
    # Download, verify, and extract the GitHub Actions runner
    RUN curl -o actions-runner-linux-x64-$RUNNER_VERSION.tar.gz -L https://github.com/actions/runner/releases/download/v$RUNNER_VERSION/actions-runner-linux-x64-$RUNNER_VERSION.tar.gz \
    && echo "$CHECKSUM actions-runner-linux-x64-$RUNNER_VERSION.tar.gz" | sha256sum -c \
    && tar xzf actions-runner-linux-x64-$RUNNER_VERSION.tar.gz \
    && rm actions-runner-linux-x64-$RUNNER_VERSION.tar.gz
    
    # Ensure the script is executable
    RUN chmod +x ./config.sh
    
    # Configure the runner with the provided script line
    RUN echo ./config.sh --unattended --url \$RUNNER_URL --token \$RUNNER_TOKEN --name \$RUNNER_NAME ${LABELS:+--labels \$LABELS} ${RUNNER_GROUP:+--runnergroup "\$RUNNER_GROUP"} ${DISABLE_UPDATE:+--disableupdate} | bash
    
    # Set the entrypoint to the run script
    ENTRYPOINT ["./run.sh"]
    EOF
    

    The Dockerfile includes:

    • A Debian base image.
    • Necessary packages like curl, tar, libicu, git, and ca-certificates.
    • A non-root user setup.
    • Node.js and NVM installation.
    • The GitHub Actions runner download and setup.

    Step 2: Configuring the Runner and GitHub Repository for Upptime

    (Skip to Step 3. if you aren't using Upptime and just want the runner build command)

    Use the upptime master repository template to create a new repository for your upptime instance setup-status-page-aws-upptime-2

    Include all branches is required for upptime to work properly, e.g. gh-pages pre-setup

    Create Github Organization (Optional) only if using custom subdomain org-create-for-subdomain-upptime

    Create a CNAME record with your provider that lines up with your domain/custom subdomain and points to your github pages url (e.g. status.qr-gen.net -> error-try-again.github.io) (Optional) setup-status-page-aws-upptime

    Select deploy from the branch gh-pages / root, add your domain, enable HTTPs and save Screenshot_20231202_170613

    Customize upptimerc.yml to your liking - this is done by editing the file in the repo or cloning the repo and editing locally update-upptimercyml

    Create a personal access token (PAT) in github with repo Read-Write access to Contents, Actions, Issues, Webhooks & Workflows - this will be used to authenticate the runner to your repo and is critical to the runner working properly pat1 pat2 pat3

    Add the token to the upptime repository secrets add-ghpat-1 add-ghpat-2 add-ghpat-3

    Step 3: Building the Docker Image

    With your Dockerfile ready, build the Docker image. This step involves passing various arguments like the runner version, checksum, and configuration details.

    docker build \
    --build-arg RUNNER_VERSION="<runner-version>" \
    --build-arg CHECKSUM="<yourchecksum>" \
    --build-arg RUNNER_URL="https://github.com/<your-user>/<custom-repo-name>" \
    --build-arg RUNNER_TOKEN="<yourtoken>" \
    --build-arg RUNNER_NAME="<custom-runner-name>" \
    --build-arg LABELS="self-hosted" \
    --build-arg RUNNER_GROUP="" \
    --build-arg DISABLE_UPDATE="true" \
    -t self-hosted-runner .
    

    Step 4: Running the Docker Container

    Once the image is built, run it as a detached Docker container. This makes your self-hosted runner operational and ready to execute workflows.

    docker run --detach self-hosted-runner
    

    Verification and Troubleshooting

    After setting up your runner, verify its registration in your GitHub repository's 'Actions' settings. If you encounter issues, check the Docker logs and ensure all the arguments were correctly passed during the build process. Additionally, check the runner's status in the 'Actions' settings of your repository. If the runner is offline, check the Docker logs for any errors. If you run into other issues, it might be helpful to re-run the Setup CI workflow, pages-build-deployment & any failed jobs, under repo Actions/All Workflows. setup-ci pages-build-deploy

    References

    © 2024 Yane ✖ Karov