Rails 8.1 Local CI: Your Laptop is Your Pipeline
October 24, 2025
âWhy trust the cloud when you can trust your overpriced laptop?â - Kim Jong Rails
The Revolution Will Be Run Locally
Rails 8.1 just dropped bin/ci, and itâs the most dictator-friendly feature since rails new.
For years, weâve been shipping our tests to GitHub Actions, CircleCI, or whatever CI service promised âgenerous free tierâ before inevitably rug-pulling us. Rails 8.1 says: run your entire CI pipeline on your own hardware.
Your M4 Max has 48GB of RAM. Time to use it.
What is bin/ci?
A simple DSL for defining your CI pipeline that runs locally or in any environment with your code.
CI.run do step "Tests: Unit", "bin/rails test" step "Tests: System", "bin/rails test:system" step "Style: Rubocop", "bin/rubocop" step "Security: Brakeman", "bin/brakeman -q"endThen run:
bin/ciAll your tests, linters, security checksâexecuted sequentially on your machine. No YAML. No waiting for runners. No âCI is downâ excuses.
Why This Matters for Self-Hosters
1. Zero CI Service Dependency
Remember when GitHub Actions had that 6-hour outage? Your local CI doesnât care. Server in a bunker? Still works. Internet down? Tests still run.
You own the pipeline.
2. Faster Feedback Loops
Cloud CI startup time:
- Spin up runner: 30s
- Checkout code: 15s
- Install dependencies: 2m
- Run tests: 45s
- Total: 3m 30s
Local CI:
- Run tests: 45s
- Total: 45s
Physics > YAML optimization.
3. Cost Savings for Heavy Workloads
GitHub Actions pricing (as of Oct 2025):
- Free tier: 2,000 minutes/month
- Paid: $0.008/minute for Linux
If you run CI 50 times a day (you deploy a lot):
- 50 runs Ă 3.5 minutes = 175 minutes/day
- 175 Ă 30 days = 5,250 minutes/month
- Cost: $42/month (after free tier)
Local CI cost:
- $0 (electricity: ~$0.50/month)
Your MacBook Pro just became infrastructure.
Setting Up bin/ci
Step 1: Create config/ci.rb
CI.run do step "Setup", "bin/setup --skip-server"
step "Tests: Models", "bin/rails test test/models" step "Tests: Controllers", "bin/rails test test/controllers" step "Tests: Jobs", "bin/rails test test/jobs"
step "Style: Rubocop", "bin/rubocop --parallel" step "Security: Brakeman", "bin/brakeman -q -z" step "Security: Bundler Audit", "bin/bundler-audit check --update"
# System tests last (slowest) step "Tests: System", "bin/rails test:system"endStep 2: Run It
bin/ciThatâs it. All steps run sequentially.
Step 3: Integrate with Git Hooks
#!/bin/bashset -e
echo "đ Running local CI before push..."bin/ci
echo "â
CI passed, pushing to remote"Now you literally cannot push broken code. If any step fails, the script exits and the push is blocked.
Advanced Patterns
Conditional Success/Failure Handling
CI.run do step "Setup", "bin/setup --skip-server" step "Tests: Rails", "bin/rails test" step "Security: Brakeman", "bin/brakeman -q"
if success? step "Signoff: All systems go", "gh signoff" else failure "Signoff: CI failed", "Fix the issues above and try again." endendThe success? check allows different behavior based on whether all previous steps passed.
Environment-Specific Steps
CI.run do step "Tests: Unit", "bin/rails test"
if ENV['CI'] # Only run slow tests in GitHub Actions step "Tests: System", "bin/rails test:system" endendThe Anti-Cloud Argument
âBut CI should run in a clean environment!â
You know what else runs in a âclean environmentâ? Production. And production breaks anyway.
Local CI philosophy:
- If it passes on your machine, it passes
- If your machine is misconfigured, fix your machine
- Dependencies match what you actually use
- Instant feedback > theoretical purity
âWhat about matrix testing (Ruby 3.3, 3.4)?â
Docker exists. You can script it outside the CI config:
#!/bin/bashfor version in 3.3 3.4; do echo "Testing Ruby $version..." docker run ruby:$version bundle exec rails testdoneOr just test on the Ruby version you actually deploy. Still faster than waiting for cloud runners.
When to Still Use Cloud CI
Real talk: Cloud CI has valid use cases.
Use cloud CI for:
- Pull request checks (external contributors)
- Multi-OS testing (Windows, macOS, Linux)
- Deployment pipelines (you donât deploy from your laptop⊠right?)
- Compliance requirements (audit logs, runner isolation)
Use local CI for:
- Pre-commit/pre-push checks
- Rapid iteration during development
- Full test suite before opening PR
- Offline development (planes, bunkers, etc.)
The Hybrid Setup
Best of both worlds:
CI.run do # Fast local checks step "Style: Rubocop", "bin/rubocop --parallel" step "Tests: Unit", "bin/rails test --exclude system"
# Save slow tests for cloud if ENV['CI'] step "Tests: System", "bin/rails test:system" step "Tests: E2E", "bin/rails test:e2e" endendLocal CI: Fast checks in 1 minute. Cloud CI: Full suite in 5 minutes.
Real-World Benchmarks
Testing on Derails codebase (M3 Max, 64GB RAM):
| Task | Cloud CI | Local CI | Speedup |
|---|---|---|---|
| Rubocop | 45s | 12s | 3.75x |
| Unit tests | 2m 15s | 38s | 3.55x |
| System tests | 4m 30s | 3m 45s | 1.2x |
| Total | 7m 30s | 4m 35s | 1.63x |
Not counting:
- Queue wait time (0-60s)
- Runner boot time (20-40s)
- Checkout time (10-20s)
Actual time saved per run: ~4 minutes
50 runs/day = 200 minutes saved = 3.3 hours back per day
The Self-Hosting Stack
Pair local CI with our infrastructure:
- Local CI (
bin/ci) - Fast feedback on your machine - Gitea Actions - PR checks on self-hosted runners
- Kamal Deploy - Push to production from
bin/deploy
Total monthly cost: âŹ3.49 (Hetzner CX23)
GitHub Actions equivalent: $50+/month
Installation
Rails 8.1+:
rails new myappcd myapp
# Create CI configcat > config/ci.rb << 'EOF'CI.run do step "Tests: All", "bin/rails test" step "Style: Rubocop", "bin/rubocop"endEOF
# Run itbin/ciExisting app:
# Add to Gemfile (if not already on Rails 8.1)bundle update rails
# Create config/ci.rb (see above)# Run bin/ciThe Philosophy
Rails 8.1âs local CI is part of a larger trend: owning your tools.
- Rails 8.0: Removed Redis/Sidekiq dependencies (SolidQueue, SolidCache)
- Rails 8.1: Removed CI service dependencies (
bin/ci) - Rails 8.2 (probably): Removes deployment platform dependencies (even more Kamal)
DHHâs vision: A single developer can build, test, and deploy a production app with zero external dependencies.
Weâre not there yet, but weâre close.
Conclusion
Is local CI for everyone? No.
Is it for dictators who:
- Deploy 20+ times a day
- Work on planes/trains/bunkers
- Distrust cloud providers
- Own expensive laptops
- Value speed over theoretical purity
Absolutely.
âThe best CI pipeline is the one that doesnât make you wait.â - BasharAlCode
Try It Yourself
# Install Rails 8.1gem install rails
# Create new apprails new my_ci_appcd my_ci_app
# Setup CIcat > config/ci.rb << 'EOF'CI.run do step "Tests: All", "bin/rails test"endEOF
# Run itbin/ciYour laptop is now your CI server. Act accordingly.