Open source · MIT licensed

CI/CD that speaks CircleCI

ForgeCI runs your .circleci/config.yml-style pipelines on your own infrastructure. A pure-Ruby compiler turns CircleCI 2.1 configs into pipelines; Docker runners execute them and stream the logs back live.

.forge-ci/config.yml
# Looks like CircleCI. That's the point.
version: 2.1

jobs:
  build:
    docker:
      - image: cimg/ruby:3.4
    steps:
      - checkout
      - run:
          name: Install dependencies
          command: bundle install
      - run:
          name: Run tests
          command: bundle exec rake test

workflows:
  build-and-test:
    jobs:
      - build

Everything a CI needs. Nothing it doesn't.

A complete continuous-integration system in two pieces: a Rails control plane and zero-dependency Docker runners.

🔄

CircleCI 2.1 compatible

A pure-Ruby, zero-dependency config compiler turns CircleCI-style YAML into executable pipelines. Bring your existing config and muscle memory.

🐳

Docker runners

Stdlib-only Ruby runners claim queued jobs and run each step in isolated Docker containers. Scale horizontally — just add more runners.

📡

Live log streaming

Watch job output in real time. Runners push log chunks over the API; the dashboard tails them via Server-Sent Events.

💾

Cache & artifacts

Dependency caches and build artifacts backed by S3, with per-project scoping and configurable retention.

🔐

Contexts & secrets

Encrypted environment contexts shared across projects, injected at runtime — with admin-gated secret rotation.

🐙

GitHub App integration

Push webhooks trigger pipelines automatically, and commit statuses report build results back to your pull requests.

How it works

Three stages, from git push to green build.

1

Push

Install the GitHub App on your repos. Every push fires a webhook; ForgeCI fetches .forge-ci/config.yml at that commit.

2

Compile

The control plane compiles your CircleCI-style config into a pipeline of jobs, workflows, and dependencies — validated before anything runs.

3

Run

Runners claim queued jobs, execute each step in Docker, and stream logs live to the dashboard while commit statuses update on GitHub.

Simple architecture

One control plane, as many runners as you need, boring dependable storage.

                    ┌─────────────────────┐
                    │     GitHub App        │
                    │  webhooks + statuses │
                    └──────────┬──────────┘
                               │ push
                    ┌──────────▼──────────┐      ┌──────────────┐
                    │   Control plane     │─────▶│   Postgres   │
                    │      (Rails 8)      │      │  jobs + state│
                    └──────────┬──────────┘      └──────────────┘
                               │ claim / heartbeat / logs
                    ┌──────────▼──────────┐      ┌──────────────┐
                    │ Runners (Docker)    │─────▶│      S3      │
                    │   zero-dep Ruby     │      │ logs/artifacts│
                    └─────────────────────┘      └──────────────┘

Quickstart

The whole stack — Postgres, control plane, worker, runner — with one compose file.

terminal
$ cp .env.example .env   # fill in S3 credentials + bucket, pick a RUNNER_TOKEN secret
$ openssl rand -hex 32   # → paste into .env as RUNNER_TOKEN
$ docker compose up --build -d
$ docker compose up -d runner
$ # → open http://localhost:3000

Object storage is AWS S3 — create the bucket first (aws s3 mb s3://<your-bucket>). The same RUNNER_TOKEN value authenticates the runner against the control plane, so there's no token to copy around. See DEPLOY.md for production deploys.