Skip to main content

On This Page

Self-Hosting GitHub Actions on a VPS: Bypassing Cloud Build Limits

3 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

CI/CD Pipeline on Your Own Server: GitHub Actions + VPS

GitHub Actions provides a robust CI/CD platform but often imposes build minute caps and storage restrictions on shared cloud runners. By migrating to a self-hosted runner on a Virtual Private Server (VPS), developers gain superuser-level access to the underlying build environment.

Why This Matters

In technical reality, shared cloud runners provide a limited experience where scaling costs increase unpredictably as build volume grows. Self-hosting transforms the build agent into a dedicated environment where specific software configurations, dedicated CPU/RAM, and direct access to internal network services—such as private registries or databases—are fully controlled by the engineer rather than restricted by a third-party provider’s environment.

Key Insights

  • GitHub Actions supports self-hosted runners on Linux, x64, and other architectures to execute jobs outside GitHub-hosted environments.
  • Docker provides isolated build environments on the runner, ensuring consistency across different VPS providers like PowerVPS or Immers Cloud.
  • The appleboy/ssh-action tool enables automated deployment to production servers via SSH secrets stored securely in GitHub.
  • Systemd services, configured via svc.sh, ensure high availability by automatically restarting the runner process after server reboots or application crashes.

Working Examples

Standard GitHub Actions workflow targeting a self-hosted runner for build and SSH deployment.

name: CI/CD Pipeline on Self-Hosted Runner
on: [push]
jobs:
  build-and-deploy:
    runs-on: [self-hosted, linux, docker]
    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 install
      - name: Build project
        run: npm run build
      - name: Deploy to server
        if: github.ref == 'refs/heads/main'
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /path/to/your/app
            git pull origin main
            npm install --production
            npm run build
            pm2 restart your-app-name

Workflow utilizing a Docker container on a self-hosted runner to ensure a clean, consistent build environment.

name: CI/CD Pipeline with Docker Runner
on: [push]
jobs:
  build-and-deploy:
    runs-on: [self-hosted, linux, docker]
    container:
      image: node:20-alpine
      options: --user node
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Install dependencies
        run: npm install
      - name: Build project
        run: npm run build

Practical Applications

  • Use Case: Running database migrations directly on a staging database by leveraging the runner’s internal network access. Pitfall: Granting excessive permissions to the CI/CD pipeline, which increases the risk of unauthorized production data corruption.
  • Use Case: Compiling large-scale projects that exceed free-tier minute limits on a dedicated VPS with high-performance CPU. Pitfall: Failing to monitor disk space for Docker images and build artifacts, which can lead to failed builds due to storage exhaustion.

References:

Continue reading

Next article

Optimizing Docker Images: A Data-Driven Guide to Reducing Image Size with Dive

Related Content