60-SECOND INSTALL Works on any CI runner. No backend required.
# Add to any job in .github/workflows/ci.yml - uses: gbudjeakp/run-right@v1 with: run: make build # ← your command here

Recommendations appear in the Job Summary tab after the run. No config, no API key, no database.

Documentation

Install RunRight

Drop one step into any CI workflow. RunRight monitors your job in the background, detects the machine it ran on, and recommends a right-sized alternative — saving you money without guesswork.

No backend or API key needed to start. Just add one step, and recommendations appear in your job log and step summary.

2 min setup

One action step. No sidecar, no DaemonSet, no infra changes.

No backend required

Recommendations work with file export alone. Backend is optional.

API key optional

Auth is disabled by default. Only enable it when you self-host.

Any runner

GitHub-hosted, self-hosted, GitLab, and Kubernetes runners are all supported.

Quick Start — No Backend Needed

The fastest path: file export only. No server, no database, no API key. The agent writes a metrics-summary.json artifact and prints recommendations directly to the job log and step summary.

Wrapper mode (recommended)

Wrap any command with run: — RunRight monitors it start to finish.

.github/workflows/ci.yml
- uses: gbudjeakp/run-right@v1
  with:
    run: make build        # ← your command here
    # export defaults to "file" — no backend needed

Standalone mode

Use step: start / step: stop to span multiple steps.

.github/workflows/ci.yml
- uses: gbudjeakp/run-right@v1
  with:
    step: start

- run: make build
- run: make test
- run: make lint

- uses: gbudjeakp/run-right@v1
  with:
    step: stop
TipRecommendations appear in the Job Summary tab of your GitHub Actions run — no extra setup required.

GitHub Actions

All inputs and their defaults:

.github/workflows/ci.yml
- uses: gbudjeakp/run-right@v1
  with:
    # ── Mode (pick one) ───────────────────────────────────
    run: ""                # wrap a single command
    step: ""               # "start" or "stop" for multi-step

    # ── Agent settings ────────────────────────────────────
    interval: "5s"         # metrics sampling interval
    duration: "0"          # max run time (0 = unlimited)
    job-id: "${{ github.run_id }}-${{ github.run_attempt }}"

    # ── Export ────────────────────────────────────────────
    export: "file"         # file | http | file,http | otlp | prometheus
    output-dir: "${{ github.workspace }}/.runright"
    upload-artifact: "true"

    # ── Backend (only needed when export includes "http") ─
    http-url: ""           # e.g. https://runright.yourcompany.com
    pr-comment: "true"     # post recommendations as a PR comment
    github-token: "${{ github.token }}"
  env:
    RUNRIGHT_API_KEY: ${{ secrets.RUNRIGHT_API_KEY }}  # only when backend has auth enabled

Using action outputs

Consume results in downstream steps:

.github/workflows/ci.yml
- uses: gbudjeakp/run-right@v1
  id: sizing
  with:
    run: make build
    export: file,http
    http-url: ${{ vars.RUNRIGHT_URL }}

- run: echo "Suggested: ${{ steps.sizing.outputs.suggested-machine }}"
- run: echo "Detected:  ${{ steps.sizing.outputs.detected-machine }}"
- run: |
    echo '${{ steps.sizing.outputs.recommendation-json }}' | jq '.[0]'

GitLab CI

Install the binary in before_script and send SIGTERM in after_script — which always runs even when the job fails, so data is captured on OOM kills and runner disconnects too.

.gitlab-ci.yml
variables:
  RUNRIGHT_URL: "${RUNRIGHT_URL}"   # set in CI/CD Variables

build:
  before_script:
    - curl -fsSL https://github.com/gbudjeakp/run-right/releases/latest/download/runright_linux_amd64 \
        -o /usr/local/bin/runright && chmod +x /usr/local/bin/runright
    - mkdir -p .runright
    - runright monitor --export file,http --http-url "$RUNRIGHT_URL" \
        --output-dir .runright --job-id "$CI_JOB_NAME-$CI_PIPELINE_ID" &
    - echo $! > .runright/monitor.pid

  script:
    - make build

  after_script:
    - kill $(cat .runright/monitor.pid 2>/dev/null) 2>/dev/null || true

  artifacts:
    paths: [.runright/]
    expire_in: 30 days
NoteSet RUNRIGHT_URL and optionally RUNRIGHT_API_KEY as masked variables in Settings → CI/CD → Variables.

Jenkins

Use Jenkins credentials bindings to inject RUNRIGHT_URL and RUNRIGHT_API_KEY, then run the monitor as a background process inside a sh block.

Jenkinsfile
pipeline {
  agent any
  environment {
    RUNRIGHT_URL     = credentials('runright-url')
    RUNRIGHT_API_KEY = credentials('runright-api-key')
  }
  stages {
    stage('Build') {
      steps {
        sh """
          curl -fsSL "https://github.com/gbudjeakp/run-right/releases/latest/download/runright_linux_amd64" \
              -o runright && chmod +x runright
          ./runright monitor --export http --http-url \$RUNRIGHT_URL &
          echo \$! > .runright.pid
          make build
          kill \$(cat .runright.pid) 2>/dev/null || true
        """
      }
    }
  }
}
NoteAdd credentials in Manage Jenkins → Credentials as "Secret text" entries with IDs runright-url and runright-api-key.

Kubernetes / Self-Hosted Runners

RunRight runs inside your CI job — not as a DaemonSet or sidecar. It auto-detects CPU and memory limits from cgroup v2 with no extra config on any K8s-hosted runner.

GitHub Actions self-hosted on K8s

.github/workflows/ci.yml
jobs:
  build:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
      - uses: gbudjeakp/run-right@v1
        with:
          run: make build
          export: file,http
          http-url: ${{ vars.RUNRIGHT_URL }}
        env:
          RUNRIGHT_API_KEY: ${{ secrets.RUNRIGHT_API_KEY }}

Optional: Downward API for guaranteed limit detection

If cgroup namespacing hides the container limits, inject them explicitly via RUNRIGHT_VCPUS and RUNRIGHT_MEMORY_GIB:

runner-pod.yaml (spec.containers[].env)
env:
  - name: RUNRIGHT_VCPUS
    valueFrom:
      resourceFieldRef:
        resource: limits.cpu
  - name: RUNRIGHT_MEMORY_GIB
    valueFrom:
      resourceFieldRef:
        resource: limits.memory
        divisor: "1Gi"
  - name: RUNRIGHT_API_KEY
    valueFrom:
      secretKeyRef:
        name: runright-secrets
        key: api-key

Helm — EKS & Any Kubernetes Cluster

The RunRight Helm chart deploys the backend and dashboard onto any Kubernetes cluster — EKS, GKE, AKS, or self-managed. It bundles a PostgreSQL subchart by default, or point it at an existing database with externalDSN.

Quick install

terminal
helm install runright ./helm/runright \
  --namespace runright --create-namespace \
  --set config.apiKey=your-secret-key

With an existing Postgres database

terminal
helm install runright ./helm/runright \
  --namespace runright --create-namespace \
  --set postgresql.enabled=false \
  --set externalDSN="postgres://user:pass@host:5432/runright?sslmode=require" \
  --set config.apiKey=your-secret-key

With an Ingress (GKE — Cloud Load Balancer)

values-gke.yaml
ingress:
  enabled: true
  className: gce
  annotations:
    kubernetes.io/ingress.class: gce
    kubernetes.io/ingress.global-static-ip-name: runright-ip
  hosts:
    - host: runright.internal.example.com
      paths:
        - path: /
          pathType: Prefix
terminal
helm install runright ./helm/runright \
  --namespace runright --create-namespace \
  -f values-gke.yaml \
  --set postgresql.enabled=false \
  --set externalDSN="$DSN"

Terraform — AWS ECS Fargate

The terraform/ module deploys RunRight on AWS Fargate with RDS PostgreSQL, Secrets Manager for credentials, CloudWatch logging, and an optional ALB target group attachment.

terraform/terraform.tfvars
name               = "runright"
vpc_id             = "vpc-0abc123"
private_subnet_ids = ["subnet-aaa", "subnet-bbb"]
allowed_cidrs      = ["10.0.0.0/8"]
db_password        = "change-me"
api_key            = "rr-secret-key"
image_tag          = "v1.0.0"
terminal
cd terraform
terraform init
terraform apply

Terraform — EKS

The terraform/eks/ module targets an existing EKS cluster. It provisions an optional RDS instance, creates a Kubernetes namespace and secret, then installs RunRight via the Helm chart.

terraform/eks/terraform.tfvars
cluster_name       = "my-eks-cluster"
vpc_id             = "vpc-0abc123"
private_subnet_ids = ["subnet-aaa", "subnet-bbb"]
eks_node_cidrs     = ["10.0.0.0/8"]
db_password        = "change-me"
api_key            = "rr-secret-key"
ingress_enabled    = true
ingress_class_name = "alb"
ingress_hostname   = "runright.internal.example.com"
terminal
cd terraform/eks
terraform init
terraform apply
TipSet create_rds = false and supply external_dsn to reuse an existing Aurora or RDS cluster shared across your platform services.

Terraform — GCP GKE

The terraform/gke/ module provisions a GKE Autopilot cluster, a Cloud SQL for PostgreSQL instance, and a Workload Identity binding so the RunRight pod can read secrets from GCP Secret Manager without a service-account key file. The Helm chart is installed via the Terraform Helm provider.

terraform/gke/terraform.tfvars
project_id         = "my-gcp-project"
region             = "us-central1"
cluster_name       = "runright-cluster"
vpc_network        = "default"
vpc_subnetwork     = "default"
db_tier            = "db-g1-small"
db_password        = "change-me"
api_key            = "rr-secret-key"
ingress_enabled    = true
ingress_class_name = "gce"
ingress_hostname   = "runright.internal.example.com"
terminal
cd terraform/gke
terraform init
terraform apply
InfoWorkload Identity is enabled by default. The module creates a GCP service account, binds it to the Kubernetes service account in the runright namespace, and grants roles/secretmanager.secretAccessor so the pod can pull credentials without a JSON key file.
TipSet create_cloud_sql = false and supply external_dsn to reuse an existing Cloud SQL or AlloyDB instance shared across platform services.

Self-Hosting the Backend

The backend gives you persistent job history, a shared dashboard, trend charts, and PR comment recommendations. It's a single Go binary + Postgres — spin it up with Docker Compose.

TipSelf-hosting takes about two minutes with Docker Compose. Once running, point every workflow at it with http-url and all job data flows to a single place your whole team can view.
docker-compose.yml
services:
  runright:
    image: ghcr.io/gbudjeakp/run-right:latest
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: postgres://runright:runright@db:5432/runright?sslmode=disable
      RUNRIGHT_API_KEY: ${RUNRIGHT_API_KEY:-}
    depends_on:
      db: { condition: service_healthy }

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB:       runright
      POSTGRES_USER:     runright
      POSTGRES_PASSWORD: runright
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U runright"]
      interval: 5s
      retries: 10

  dashboard:
    image: ghcr.io/gbudjeakp/run-right-dashboard:latest
    ports:
      - "3000:3000"

volumes:
  pgdata:
terminal
docker compose up -d

# optional: seed with demo data
go run ./cmd/seed/ --url http://localhost:8080

API Key — When You Need It

The API key is a server-side guard. It has nothing to do with getting recommendations — you get full machine sizing output without ever setting one. It only matters when you self-host the backend and want to restrict who can write to your dashboard.

ScenarioNeed API key?Why
File export only (no backend)NoData stays local — nothing authenticates
Backend, RUNRIGHT_API_KEY unsetNoAuth is disabled — dev mode
Backend, RUNRIGHT_API_KEY setYes — set the same key in CIAgent sends it as Authorization: Bearer
Dashboard loginYes — if backend has a keyLogin screen checks it against the server
.github/workflows/ci.yml
- uses: gbudjeakp/run-right@v1
  with:
    run: make build
    export: file,http
    http-url: ${{ vars.RUNRIGHT_URL }}
  env:
    RUNRIGHT_API_KEY: ${{ secrets.RUNRIGHT_API_KEY }}
ImportantTreat the API key like a password. Use your platform's secret store (GitHub Secrets, GitLab Variables, K8s Secrets) and never commit it to source control.

Environment Variables

VariableWhereRequiredDescription
RUNRIGHT_API_KEYServer + CI agentNoShared secret for auth. Set on the server to require auth; set in CI so the agent can post results. If unset everywhere, auth is disabled.
DATABASE_URLServer onlyYes (server)Postgres DSN. Example: postgres://runright:runright@localhost:5432/runright?sslmode=disable
RUNRIGHT_VCPUSCI agent (K8s)NovCPU count for machine detection. Inject via Downward API when cgroup detection is unreliable.
RUNRIGHT_MEMORY_GIBCI agent (K8s)NoMemory limit in GiB for machine detection. Inject via Downward API. Overrides cgroup-based detection when set.
OTEL_EXPORTER_OTLP_ENDPOINTCI agentNoOTLP collector endpoint for the otlp export backend. Example: http://localhost:4317

CLI Reference

runright monitor

flags
runright monitor [flags]

  --export           string     Export backends, comma-separated: file,http,otlp,prometheus (default "file")
  --http-url         string     Backend base URL for http export
  --interval         duration   Sampling interval (default 5s)
  --duration         duration   Stop after this duration (0 = run until SIGTERM/SIGINT)
  --job-id           string     Job identifier (default: timestamp-based ID)
  --output-dir       string     Directory for file output (default ".")
  --prometheus-port  int        Port for Prometheus /metrics endpoint (default 9090)

runright recommend

flags
runright recommend [flags]

  --metrics   string   Path to metrics-summary.json (default "metrics-summary.json")
  --provider  string   Filter by provider: aws, gcp, or github (default: all)
  --format    string   Output format: table, json, or markdown (default "table")

runright serve

flags
runright serve [flags]

  --port  int   HTTP port (default 8080)