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.
One action step. No sidecar, no DaemonSet, no infra changes.
Recommendations work with file export alone. Backend is optional.
Auth is disabled by default. Only enable it when you self-host.
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.
- 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.
- 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
GitHub Actions
All inputs and their defaults:
- 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:
- 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.
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
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.
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
"""
}
}
}
}
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
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:
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
helm install runright ./helm/runright \ --namespace runright --create-namespace \ --set config.apiKey=your-secret-key
With an existing Postgres database
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)
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
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.
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"
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.
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"
cd terraform/eks terraform init terraform apply
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.
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"
cd terraform/gke terraform init terraform apply
runright namespace, and grants roles/secretmanager.secretAccessor so the pod can pull credentials without a JSON key file.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.
http-url and all job data flows to a single place your whole team can view.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:
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.
| Scenario | Need API key? | Why |
|---|---|---|
| File export only (no backend) | No | Data stays local — nothing authenticates |
| Backend, RUNRIGHT_API_KEY unset | No | Auth is disabled — dev mode |
| Backend, RUNRIGHT_API_KEY set | Yes — set the same key in CI | Agent sends it as Authorization: Bearer |
| Dashboard login | Yes — if backend has a key | Login screen checks it against the server |
- 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 }}
Environment Variables
| Variable | Where | Required | Description |
|---|---|---|---|
RUNRIGHT_API_KEY | Server + CI agent | No | Shared 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_URL | Server only | Yes (server) | Postgres DSN. Example: postgres://runright:runright@localhost:5432/runright?sslmode=disable |
RUNRIGHT_VCPUS | CI agent (K8s) | No | vCPU count for machine detection. Inject via Downward API when cgroup detection is unreliable. |
RUNRIGHT_MEMORY_GIB | CI agent (K8s) | No | Memory limit in GiB for machine detection. Inject via Downward API. Overrides cgroup-based detection when set. |
OTEL_EXPORTER_OTLP_ENDPOINT | CI agent | No | OTLP collector endpoint for the otlp export backend. Example: http://localhost:4317 |
CLI Reference
runright monitor
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
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
runright serve [flags] --port int HTTP port (default 8080)