//Servers

Run a Self‑Hosted CI/CD Pipeline with GitHub Actions on VPS

Learn how to set up self‑hosted GitHub Actions runners on an AtoZNode VPS in India, covering installation, security, and workflow integration.

6 min read
Run a Self‑Hosted CI/CD Pipeline with GitHub Actions on VPS

Running a CI/CD pipeline on your own infrastructure gives you complete control over build environments, network latency, and cost structure. By deploying self‑hosted GitHub Actions runners on a dedicated VPS from AtoZNode, you keep sensitive code inside your data centre while still using GitHub’s workflow engine. This guide walks you through the full process – from provisioning a VPS to registering the runner and linking it to your repositories.

1. Why Use Self‑Hosted Runners on a Dedicated VPS?

  • Performance control: Choose the exact CPU, RAM, and storage you need for heavy builds or parallel jobs.
  • Network proximity: Keep traffic within your own region (e.g., Mumbai or Delhi) for faster clone and deployment times.
  • Custom environment: Pre‑install proprietary tools, licenses, or internal libraries that are not allowed on GitHub‑hosted runners.
  • Cost predictability: Pay a fixed monthly fee for the VPS instead of per‑minute charges for hosted runners.

2. Prerequisites

  1. AtoZNode dedicated VPS with root (or sudo) access. The steps below cover both Debian/Ubuntu (apt) and AlmaLinux/Rocky/RHEL (dnf).
  2. A GitHub account with admin rights on the repository or organization where you want to add the runner.
  3. Basic knowledge of the Linux command line and SSH.

3. Preparing the VPS

3.1 Update the system

Keeping packages up‑to‑date reduces the risk of missing dependencies later.

Debian / Ubuntu (apt)
# apt update && apt upgrade -y
# apt install -y curl git ca-certificates
AlmaLinux / Rocky / RHEL (dnf)
# dnf update -y
# dnf install -y curl git ca-certificates

3.2 Create a dedicated user for the runner

Running the runner as a non‑root user follows the principle of least privilege.

Both Debian/Ubuntu and AlmaLinux/Rocky/RHEL
# adduser --system --group --home /opt/actions-runner actionsrunner
# usermod -aG docker actionsrunner   # optional, if you need Docker inside the runner

4. Installing the GitHub Actions Runner

4.1 Download the latest runner package

GitHub publishes a tarball for each architecture. The URL below always points to the latest stable version.

Both OS families
# su - actionsrunner
$ cd /opt/actions-runner
$ curl -L -o actions-runner-linux-x64.tar.gz \
    https://github.com/actions/runner/releases/latest/download/actions-runner-linux-x64.tar.gz
$ tar xzf actions-runner-linux-x64.tar.gz

4.2 Install required dependencies

The runner needs a few runtime libraries. Install them according to your distribution.

Debian / Ubuntu (apt)
$ sudo apt install -y libicu70 libssl1.1
AlmaLinux / Rocky / RHEL (dnf)
$ sudo dnf install -y libicu openssl-libs

4.3 Configure the runner

Generate a registration token from GitHub, then run the configuration script. The token is short‑lived, so copy it quickly.

  1. Navigate to Settings → Actions → Runners in your repository or organization.
  2. Click Add runner, select Linux, and copy the displayed command lines.

Assuming you are using a repository‑level runner, the commands look like this:

Both OS families
$ ./config.sh --url https://github.com/your-org/your-repo \
    --token YOUR_TOKEN_HERE \
    --name vps-runner-01 \
    --work _work \
    --labels self-hosted,linux,vps

The script asks a few questions:

  • Runner name: an identifier that appears in the GitHub UI.
  • Work folder: where the runner stores job artifacts.
  • Labels: custom tags you can reference in workflow runs-on clauses.

4.4 Set up the runner as a systemd service

Running the runner as a service ensures it starts on boot and restarts after failures.

Both OS families
$ sudo ./svc.sh install
$ sudo ./svc.sh start

Check the status with systemctl status actions.runner.*. The service runs under the actionsrunner user created earlier.

5. Securing and Scaling Your Runner

5.1 Restrict network access

Limit inbound traffic to SSH (port 22) from your own IP range. On AtoZNode you can adjust firewall rules via the control panel or iptables:

Both OS families (example with iptables)
# iptables -A INPUT -p tcp -s YOUR_IP/32 --dport 22 -j ACCEPT
# iptables -A INPUT -p tcp --dport 22 -j DROP
# iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# iptables -P INPUT DROP

5.2 Use a non‑root Docker socket (if Docker is needed)

Instead of giving the runner full root access, add the runner user to the docker group (see step 3.2). Ensure the Docker daemon is configured with --group docker or the default group is docker.

5.3 Horizontal scaling

For high‑throughput pipelines, spin up additional VPS instances and repeat the registration steps with unique names and labels (e.g., vps-runner-02, vps-runner-03). In your workflow, you can target a specific subset:

runs-on: [self-hosted, linux, vps]

GitHub will distribute jobs across all matching runners, balancing the load automatically.

6. Using the Self‑Hosted Runner in a GitHub Actions Workflow

Below is a minimal example that builds a Node.js app, runs tests, and pushes a Docker image to a private registry. Save it as .github/workflows/ci.yml in your repository.

name: CI on Self‑Hosted VPS

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: [self-hosted, linux, vps]   # selects the runners we created
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Build Docker image
        run: |
          docker build -t registry.example.com/myapp:${{ github.sha }} .
          docker push registry.example.com/myapp:${{ github.sha }}

Because the runner resides on your VPS, the Docker daemon has direct access to your private registry without additional authentication steps, provided you have configured ~/.docker/config.json for the actionsrunner user.

7. Maintenance Tips

  • Regular updates: Periodically pull the latest runner release (download, extract, and run ./svc.sh restart) to stay compatible with GitHub.
  • Log rotation: The runner writes logs to /opt/actions-runner/_diag. Set up logrotate to prevent disk exhaustion.
  • Backup configuration: Store the .runner folder (contains the registration token) in a secure location; you can reuse it if you need to rebuild the VPS.
  • Monitor health: Use tools like htop, netstat, or AtoZNode’s monitoring panel to watch CPU, memory, and network usage during peak builds.

Conclusion

Deploying self‑hosted GitHub Actions runners on AtoZNode dedicated VPS instances gives Indian developers a powerful, cost‑effective way to run CI/CD pipelines under full control. By following the steps above – updating the system, installing the runner, securing the environment, and integrating it into your workflows – you can achieve reliable builds, faster deployments, and compliance with internal policies. As your projects grow, simply add more VPS runners and let GitHub’s scheduler balance the workload across them.

gitlab-cigithub-actionsself-hosted-runnervpslinuxci-cddockerubuntu

Try it on your own server

Follow along on a Cloud VPS with full root access, or read the step-by-step knowledge base guides.

Guides: VPS for WordPress · Node.js hosting · VPS or dedicated server · Paying by UPI