Octo + GitHub Actions

Run your CI workflows on your own hardware.
Trigger, monitor, and manage Octo jobs directly from GitHub.

Why Use Octo with GitHub Actions?

Octo transforms your own infrastructure into a powerful CI/CD execution layer, eliminating expensive cloud runner costs while maximizing your hardware investment.

πŸ’»

Efficient Infrastructure Utilization

Instead of paying per-minute for cloud runners, leverage your existing servers and hardware. Octo lets you run CI/CD jobs directly on your infrastructure, ensuring 100% utilization of your compute resources.

πŸ’°

Eliminate Cloud Runner Costs

GitHub Actions cloud runners are expensive and limited. With Octo, you eliminate per-minute billing and runner queuing. Your infrastructure becomes your CI/CD powerhouse β€” fully under your control.

⚑

Unlimited Parallelism

Scale your CI/CD pipelines without hitting runner limits or paying premium rates. Run hundreds of parallel jobs across your infrastructure β€” limited only by your hardware.

πŸ”’

Full Control & Security

Keep your code and data on your own infrastructure. No data leaving your network, no vendor lock-in, complete transparency in how your CI/CD pipeline executes.

The Cost Advantage

GitHub Actions Cloud Runners: $0.008 per minute per runner (can add up fast)

Octo on Your Hardware: Fixed infrastructure cost, unlimited CI/CD runs

For teams running hundreds of CI/CD jobs monthly, Octo can save thousands in runner costs.

One-click Runs

Trigger Octo jobs directly from a GitHub workflow with a single command.

Fully Utilize Your Infrastructure

Unleash the full potential of your infrastructure β€” no cloud limits, no wasted compute.

Parallel Execution

Run multiple Octo jobs in parallel to dramatically speed up CI pipelines.

Overview β€” Integrating Octo into GitHub Workflows

Octo seamlessly integrates with GitHub Actions, allowing you to run your CI/CD workflows on your own hardware. You can add more runners if you need additional parallelism or want to distribute workloads across different environments. It's also possible to set up Octo in a cluster configuration to scale out execution even further.

❌ Without Octo: Sequential Execution

By default, GitHub Actions executes jobs sequentially, one after another. While this is simple and reliable, it takes significantly longer. In this example, software build takes nearly 4 minutes and testing around 17 minutes β€” all running back-to-back with no parallelism.

Old Workflow Without Octo - Sequential Execution

Sequential: Build β†’ Test β†’ Done (~21 minutes total)

βœ… With Octo: Parallel Execution

Octo dispatches jobs to external nodes and runs them in parallel, dramatically reducing total execution time. The GitHub runner remains stable and can process additional jobs without interruption. The same pipeline completes in a fraction of the time with full resource utilization.

New Workflow With Octo - Parallel Execution

Parallel: Build + Test simultaneously (Significantly faster)

πŸš€ With Octo: Cluster Setup

For even greater scalability, Octo supports cluster configurations across multiple machines. Distribute workloads across your entire infrastructure and achieve unlimited parallelism with complete fault tolerance and load balancing.

New Cluster Setup With Octo - Unlimited Parallelism

Cluster: Unlimited parallel jobs

How Octo supercharges your CI/CD pipeline

Octo seamlessly integrates with GitHub Actions, transforming your own infrastructure into a high-performance execution layer for fast, efficient, and fully controlled automation.

βš™οΈ

Parallel Execution

The Smoketest Workflow orchestrates multiple jobs simultaneously in parallel, executing them directly on your Octo runner with zero wait times.

πŸš€

Use Your Own Power

Skip cloud queues and runner limits β€” Octo connects GitHub Actions to your real computing power. Each region runs independently using its own secure token and isolated environment.

πŸ“Š

Instant Results

Once both runs are complete, Octo automatically uploads all test reports back to GitHub, giving you instant, centralized insights β€” right where you work.

Example GitHub Actions Workflow

Below is the shown workflow of how Octo can enable parallel execution within a single CI job.


# ============================================================
# LINUX BUILD (Octo parallel)
# ============================================================
  build-linux:
    if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }}
    name: Build Linux Installers
    runs-on: [self-hosted, windows, x64, octo]
    env:
      LOCAL_DEB_CACHE: C:\octo\artefacts\${{ github.run_id }}\linux
    steps:
      - uses: actions/checkout@v4
      
      - name: Octo login
        shell: powershell
        run: |
          octo login --token $env:OCTO_TOKEN --server $env:OCTO_SERVER
          octo config --docker python:3.11
      
      - name: Build Linux packages in parallel
        shell: powershell
        run: |
          $root = Get-Location
          $jobs = @()
          foreach ($c in @("client","server","runner")) {
            $jobs += Start-Process powershell `
              -ArgumentList "-Command cd `"$root/$c`"; octo run ./start_build.sh" `
              -PassThru
          }
          $jobs | Wait-Process
      
      - name: Collect .deb files into docker_debs/
        shell: powershell
        run: |
          Write-Host "Collecting .deb files..."
          if (Test-Path docker_debs) {
            Remove-Item docker_debs -Recurse -Force
          }
          New-Item -ItemType Directory -Force -Path docker_debs | Out-Null
          $debFiles = Get-ChildItem -Recurse -Filter *.deb |
            Where-Object { $_.FullName -notmatch '\\docker_debs\\' }
          if (-not $debFiles) {
            Write-Error "No .deb files found!"
            exit 1
          }
          foreach ($file in $debFiles) {
            Copy-Item $file.FullName docker_debs -Force
          }
          Get-ChildItem docker_debs

# ============================================================
# Test Set (PARALLEL, OCTO ORCHESTRATED)
# ============================================================
  testset:
    name: Run Octo Test Set (parallel)
    runs-on: [self-hosted, windows, x64, octo]
    needs:
      - wsl-infra-up
      - build-docker
    if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }}

    steps:
      - uses: actions/checkout@v4

      - name: Configure Octo
        shell: powershell
        run: |
          octo login --token $env:OCTO_TOKEN --server $env:OCTO_SERVER
          octo config --docker octo-test-base

      - name: Run tests in parallel via Octo
        shell: powershell
        run: |
          # Funktion zum Parsen von JUnit XML
          function Get-TestCasesFromJUnit {
              param([string]$xmlPath)
              
              if (-not (Test-Path $xmlPath)) {
                  return @()
              }
              
              try {
                  [xml]$xml = Get-Content $xmlPath -Raw -Encoding UTF8
                  $cases = @()
                  
                  foreach ($testcase in $xml.testsuites.testsuite.testcase) {
                      $cases += [PSCustomObject]@{
                          name    = $testcase.name
                          time    = $testcase.time
                          failure = $testcase.failure
                          skipped = $testcase.skipped
                      }
                  }
                  
                  return $cases
              }
              catch {
                  Write-Warning "Could not parse JUnit XML: $xmlPath"
                  return @()
              }
          }
          
          # Hauptlogik
          $root = Get-Location
          $tests = @(
            "smoketest.py",
            "test_daily.py",
            "test_file_upload.py",
            "test_docker.py",
            "test_install_feat.py"
          )
          $jobs = @()
          $exitCodeFiles = @()
          New-Item -ItemType Directory -Force test-reports | Out-Null
          
          foreach ($test in $tests) {
            $exitFile = "$env:TEMP\exit-$test.txt"
            $exitCodeFiles += $exitFile
            $script = @"
          cd '$root/test'
          octo run $test
          `$LASTEXITCODE | Out-File '$exitFile' -NoNewline
          exit `$LASTEXITCODE
          "@
            $jobs += Start-Process powershell `
              -ArgumentList "-Command", $script `
              -NoNewWindow `
              -PassThru
          }
          $jobs | Wait-Process
          
          # GitHub Job Summary
          $summary = "## Octo Test Summary`n`n"
          $summary += "| Testfile | Status |`n"
          $summary += "|----------|--------|`n"
          
          for ($i = 0; $i -lt $tests.Count; $i++) {
              $test = $tests[$i]
              $code = [int](Get-Content $exitCodeFiles[$i] -Raw)
              $name = [System.IO.Path]::GetFileNameWithoutExtension($test)
              $xmlPath = "test-reports/junit-$name.xml"
              
              if ($code -eq 0) {
                  $summary += "| $test | :white_check_mark: PASS |`n"
              } else {
                  $summary += "| $test | :x: FAIL |`n"
              }
              
              # Einzelne TestfΓ€lle (Details)
              $cases = Get-TestCasesFromJUnit $xmlPath
              if ($cases.Count -gt 0) {
                  $summary += "`n
`n" $summary += "Testcase Overview`n`n" $summary += "| Testcase | Status | Duration (s) |`n" $summary += "|----------|--------|--------------|`n" foreach ($case in $cases) { $status = ":white_check_mark: PASS" if ($case.failure) { $status = ":x: FAIL" } elseif ($case.skipped) { $status = ":fast_forward: SKIP" } $summary += "| $($case.name) | $status | $($case.time) |`n" } $summary += "`n
`n" } } $summary | Out-File ` -FilePath $env:GITHUB_STEP_SUMMARY ` -Encoding utf8 ` -Append # ------------------------------------------------- # Upload Reports (7 Tage) # ------------------------------------------------- - name: Upload pytest reports if: always() uses: actions/upload-artifact@v4 with: name: pytest-reports retention-days: 7 path: | test/test-reports/*.xml test/test-reports/*.html