← All projects
Build 02Distributed CI/CD platformExecution core built · GitHub runtime live

PulseGrid

“I pushed my code and it got tested.” I built everything underneath that sentence.

A lightweight distributed CI platform, like GitHub Actions or Buildkite, where I built the infrastructure myself: webhooks, a DAG workflow engine, a resource-aware scheduler, pull-based Docker workers, and leases that survive crashes.

RoleSolo · systems design to code
LikeGitHub Actions · Buildkite
CoreScheduler · Workers · Leases
Tested byChaos demo · e2e acceptance
StackPython · FastAPI · SQLite WAL · Docker
01 · The ideaPush to result

The infrastructure behind CI, not the UI around it.

Everyone uses CI. Few people know what happens between git push and a green check. I wanted to build that part myself, end to end, so every step below is real code in PulseGrid.

01GitHub push

A developer pushes code

02Webhook

Signature verified, retries de-duplicated

03Control plane

Finds the repo and exact commit

04Workflow DAG

Jobs with real dependencies

05Scheduler

Matches jobs to CPU, memory, GPU

06Worker

Checks out the exact SHA

07Docker

Isolated run, logs back

08Result

Back to the developer

02 · The experienceWhat a developer sees

Simple on top. Serious underneath.

Connect a repo, add a small workflow file, push. From there it's a live view of every job, the machines running them and the result back on GitHub.

PULSEGRID · WORKFLOW RUNCONCEPT UI · ILLUSTRATIVE DATA
Workflow #1842● RUNNING
COMMIT a31d92f · BRANCH main · ci.yml
✓lint11s
✓tests43s
●buildrunning · worker-b
○deploywaiting on build
BUILD · LIVE LOGS
00:00 claimed by worker-b lease 7f3c…00:01 git checkout a31d92f (exact SHA ✓)00:02 docker run python:3.12-slim00:02 mount /workspace (read-only)00:04 pip install -r requirements.txt00:19 building wheel…00:21 heartbeat ✓ lease renewed00:26 ▌

Watch every job live

Each run shows the exact commit, every job's state and timing, and real-time logs for any job you open.

Worker A● HEALTHY
CPU3 / 4
MEMORY5 / 8 GB
running · tests #1843
Worker B● HEALTHY
CPU6 / 8
MEMORY9 / 16 GB
running · build #1842
Worker C○ IDLE
GPU0 / 1
MEMORY0 / 32 GB
waiting for GPU jobs

See the machines

Workers advertise CPU, memory, GPU and health. The scheduler only hands a job to a worker that can actually fit it.

fix: retry flaky upload testa31d92f · main
●PulseGrid CI — PendingON PUSH
✓PulseGrid CI — PassedWHEN IT FINISHES
✗PulseGrid CI — FailedOR THIS

Results where you work

Status goes back to the commit on GitHub, with durable delivery: if GitHub is down, the update retries, and an old result can never overwrite a newer one.

PRODUCT DIRECTION · DESIGNED FOR THIS PAGETODAY: API + CLI
03 · The part I care about mostFailure handling

Go ahead. Crash a worker.

I didn't want a system that only works when everything behaves. Every job runs on a lease. Workers must keep renewing it with heartbeats, or PulseGrid takes the job back and gives it to someone else.

Job 42 · buildT+00s
CONTROL PLANELEASE: —WORKER AIDLEWORKER BIDLE✕ CRASHEDSTALE RESULT → REJECTED
What happens
01
Worker A pulls Job 42 and gets lease 7f3c
02
Heartbeats renew the lease while it runs
03
Worker A crashes. Heartbeats stop.
04
Lease expires. Resources released exactly once.
05
Job 42 requeued, retry count +1, new lease 9a1e
06
Worker B runs Job 42 and finishes
07
Worker A comes back with old lease 7f3c. Fenced out.
Why it matters

The tricky part isn't retrying. It's making sure the old worker can't come back and overwrite the new result. Every heartbeat, log and completion call carries its lease ID, so a stale attempt is rejected. A chaos demo in the repo reassigns a job with a new lease after a worker expires.

04 · Workflows as graphsNot a list of commands

It understands which job waits for which.

A small YAML file becomes a directed acyclic graph. Lint and tests run in parallel, build waits for both, deploy waits for build. The engine decides what's ready, the scheduler decides where it fits.

# .pulsegrid/workflow.yml
name: ci
jobs:
  lint:
    image: python:3.12-slim
    command: ruff check .
  tests:
    image: python:3.12-slim
    command: pytest
  build:
    image: python:3.12-slim
    command: python -m build
    needs: [lint, tests]
  deploy:
    image: alpine:3.20
    command: ./deploy.sh
    needs: [build]
lint ✓tests ✓build ●deploy ○QUEUED → DONEREADY WHEN BOTH PASSBLOCKED
NO PARENTSStart queued immediately
HAS PARENTSBlocked until every parent succeeds
PARENT FAILSDescendants skipped, other branches keep going
05 · Built vs. visionNo overselling

The hard infrastructure already runs.

The execution core and the GitHub runtime are working today. The product layer that teams would pay for comes next.

Built 08

Control plane with durable job queueLIVE
Pull-based Docker workers, CPU/memory/GPU matchingLIVE
Leases, heartbeats & stale-worker fencingLIVE
Crash recovery with retry budgetsLIVE
YAML workflows as a DAGLIVE
Verified GitHub webhooks, no duplicate runsLIVE
Exact-SHA checkout, read-only workspaceLIVE
Container logs to the control planeLIVE

Next 06

Commit status on GitHub with durable deliveryNEXT
Live dashboard & real-time log streamingPLANNED
Workers on separate Linux machinesPLANNED
Sign-in, organizations, GitHub AppPLANNED
Private repos & encrypted secretsPLANNED
Autoscaling workersFUTURE
1×resources released exactly once on every recovery
0duplicate workflows from GitHub retries
SHAtests the exact pushed commit, never “latest”
Chaosdemo reassigns a job after a worker dies
06 · ArchitectureControl plane + workers

The brain coordinates. Workers just run.

The control plane owns workflow state, scheduling, leases, retries and GitHub. Workers only provide compute and run jobs safely. That split is what lets it grow from three workers to hundreds without changing anything for the developer.

CONTROL PLANESTATE · SCHEDULER · LEASES · RETRIES · GITHUBPULL · HEARTBEATWORKER A4 CPU · 8 GBDOCKERWORKER B8 CPU · 16 GBDOCKERWORKER CGPU · 32 GBDOCKER+
07 · The visionWhat a serious CI needs

Everything developers expect, on the same core.

The production version adds the features teams rely on, plus observability, so when a pipeline suddenly takes ten minutes you can see why.

SECURITY
Secrets & private repos

Encrypted secrets, private checkout, scoped GitHub App access

SPEED
Caching & artifacts

Dependency and Docker-layer caches, build artifacts

CONTROL
Limits & rules

Cancellation, timeouts, concurrency limits, branch rules

SCALE
Autoscaling

Add compute when the queue is busy, shut it down when quiet

REUSE
Reusable workflows

Shared pipelines across repositories

TEAMS
Orgs & sign-in

Organizations, roles, repository permissions

INSIGHT
Observability

Queue depth, worker health, utilization, failure and retry rates

HISTORY
Analytics

Workflow duration and performance over time

Not just a script that runs Docker. A small, real distributed execution platform.

PythonFastAPISQLite WALDockerYAML DAGsGitHub webhooksHMAC-SHA256GitHub Contents API
↑↓ NAVIGATE↵ OPENESC CLOSE