"GitHub is down again." That one Slack message. At 2 AM. Right before a production deploy. That was the last straw.
The Incident That Started Everything
It was a regular Wednesday night. I had a hotfix ready. CI was green locally. All I needed was to push, trigger the pipeline, and ship.
Then GitHub decided it didn't exist anymore.
No push. No pull. No Actions. Nothing. Just a sad little status page telling me "We're investigating an incident affecting Git operations." Cool. Super helpful. I'll just sit here and watch my deploy window close.
I've been a GitHub user for years. I love it, genuinely. But that night, staring at that status page, I kept thinking — why am I this dependent on a single platform I have zero control over?
So I did what any sleep-deprived developer does at 2 AM.
I opened a new terminal and typed mkdir OpencodeHub.
What Is OpenCodeHub?
OpenCodeHub is a fully self-hosted Git platform I built from scratch. Think GitHub — but running on your server, under your control, with your rules.
No more status pages. No more downtime surprises. No more "we're investigating."
The tagline I eventually landed on:
"A modern, self-hosted Git platform for teams that want speed, control, and clean workflows."
But honestly? The real tagline is: "I was angry and I had a weekend."
The Tech Stack (Because You're Going to Ask)
I didn't want to reinvent every wheel. I wanted to build something I'd actually enjoy maintaining. So I made some deliberate choices:
| Layer | Choice |
|---|---|
| Framework | Astro 4.x (SSR, Node standalone) |
| UI | React 18 + Tailwind CSS + Radix UI |
| Database | Drizzle ORM (PostgreSQL, SQLite, Turso) |
| Auth | JWT + bcryptjs + TOTP + OAuth (arctic) |
| Git | Native git CLI + simple-git + isomorphic-git |
| SSH Server | ssh2 library |
| CI/CD | Dockerode (Docker API) |
| Queue | BullMQ + Redis |
| CLI | Commander.js + Inquirer |
Why Astro? Because I wanted SSR without drowning in a React SPA for everything. Astro lets me drop React components exactly where I need them. It's been a genuinely pleasant choice — fast builds, clean routing.
Why Drizzle? Because Prisma was too opinionated for what I needed and raw SQL makes me sad at 3 AM. Drizzle is just TypeScript all the way down. I can switch between PostgreSQL for production, SQLite for local dev, and Turso for edge deployments. That flexibility mattered.
Features I Actually Shipped
This wasn't a weekend toy that I abandoned. Here's what's actually working:
Core Git Platform
- HTTP + SSH git push/pull — yes, real SSH on port 2222, not just HTTPS
- Pull Requests — comments, reviews, drafts, approvals, suggested changes
- Issues — labels, milestones, project boards
- Wiki — repository wiki with full revision history
- Organizations — teams, collaborators, granular repository settings
- Branch Protection — required reviews, status checks, push restrictions
The Thing I'm Most Proud Of: Stacked PRs
If you've ever used Graphite, you know the power of stacked PRs. Instead of one monolithic 3,000-line PR that nobody wants to review, you break your work into a clean stack of focused, reviewable pieces.
I built this into OpenCodeHub natively — both in the web UI and the CLI.
# Create a stack
och stack create feature/auth-step-1
# Submit the whole stack for review
och stack submit
# Keep it in sync as you iterate
och stack sync
This alone was worth building the whole thing.
CI/CD Pipelines
I made the pipelines GitHub Actions-compatible. Same YAML syntax. Same structure. The idea was: if you're migrating from GitHub, your workflows should just... work. No rewriting everything from scratch.
The runner uses Docker via Dockerode under the hood. Each job gets its own container. Clean, isolated, reproducible.
AI Code Review
I added AI-powered code review that runs before human reviewers even see the PR. It catches the obvious stuff — security issues, logic bugs, style inconsistencies — so human review can focus on architecture and intent.
The Merge Queue
Stack-aware merge queue with speculative builds and priority lanes. If you have a stack of 5 PRs, the queue knows about their dependencies and builds them intelligently. No more merge conflicts from PRs stepping on each other.
140+ REST API Routes + GraphQL
# Create a repo
curl -X POST http://localhost:4321/api/repos \
-H "Authorization: Bearer <token>" \
-d '{"name":"my-project","visibility":"public"}'
# List pull requests
curl http://localhost:4321/api/repos/alice/my-project/pulls \
-H "Authorization: Bearer <token>"
There's also a full GraphQL endpoint and an auto-generated OpenAPI spec. Because I hate undocumented APIs as much as the next person.
The Architecture
Clients (Browser / Git CLI / och CLI)
│
▼
OpenCodeHub Platform (Astro SSR + React)
├── Web UI
├── REST API (140+ routes)
├── GraphQL Endpoint
├── Git HTTP Server
└── SSH Git Server (port 2222)
│
▼
Persistence Layer
├── PostgreSQL / SQLite / Turso (Drizzle ORM)
├── Redis (sessions, queues, caching)
└── Pluggable Storage (S3, GCS, Azure, local…)
Four separate runtime processes keep things clean:
| Process | Command | Purpose |
|---|---|---|
| Main App | npm run dev |
Web UI + API + Git HTTP |
| SSH Git | npm run git:start |
SSH git push/pull |
| Worker | npm run worker:start |
Background jobs |
| Runner | npm run runner:start |
CI/CD pipeline execution |
Getting It Running (Seriously, 5 Minutes)
# Clone it
git clone https://github.com/swadhinbiswas/OpencodeHub.git
cd OpencodeHub
# Configure
cp .env.example .env
# Edit .env with your settings
# Start everything
docker-compose up -d
# Create your admin user
docker-compose exec app bun run scripts/seed-admin.ts
# Open it
open http://localhost:4321
That's it. You now have your own Git platform running locally.
For production you'll want PostgreSQL over SQLite, Redis, HTTPS via Nginx or Caddy, and external storage like S3 or MinIO. The deployment guide covers all of that, but the bones are solid out of the box.
The CLI (och)
I published the companion CLI as opencodehub-cli on npm:
npm install -g opencodehub-cli
# Authenticate against your instance
och auth login --url http://your-server.com
# The interactive cockpit — my personal favorite
och focus
20+ command groups covering stacks, merge queues, repos, issues, and more. Because clicking through a UI when you're deep in a terminal session is a crime.
What I Learned Building This
1. SSH is not that scary. I put off implementing SSH git for weeks because
I thought it would be brutal. The ssh2 library made it surprisingly
manageable. The tricky part was git's internal protocol, not SSH itself.
2. Drizzle ORM is criminally underrated. Type-safe queries, multiple database backends, migrations that don't feel like a punishment. I'm a convert.
3. BullMQ + Redis is the right answer for background jobs. I tried doing things without a proper queue first. That was a mistake. BullMQ is robust and the developer experience is clean.
4. Spec compatibility matters more than I thought. Making the CI pipelines GitHub Actions-compatible was extra work upfront, but it means people can actually migrate without a complete rewrite. That was the right call.
5. Storage adapters deserve abstraction from day one. I built the abstract
StorageAdapter pattern early and it's paid off. I support 8+ backends —
S3, GCS, Azure, Google Drive, OneDrive, Dropbox, Rclone, and local — all
behind the same interface.
Is It Production Ready?
Mostly. I have 271 commits, a proper test suite (Vitest + Playwright), load
tests, a PRODUCTION_READINESS.md checklist, and a CircleCI pipeline. There
are 67 open issues — mostly planned features and polish items, not fires.
Would I run my team's code on it today? Yes, with proper backups and a reverse proxy in front.
Would I run a Fortune 500's engineering department on it? Give me another six months.
Why Not Just Use Gitea / Forgejo / GitLab?
Fair question. I tried them. They're good. But they didn't have native stacked PRs, and the CI/CD story always felt bolted on. I also wanted to build something in a modern TypeScript stack end-to-end, using tools I actually enjoy.
Sometimes you build a thing not just because it doesn't exist, but because you building it teaches you things no existing tool can.
Try It / Contribute
The repo is open source under MIT:
→ github.com/swadhinbiswas/OpencodeHub
Docs live at docs.opencodehub.space
If you're tired of GitHub downtime, if you have data residency requirements, if you want stacked PRs without paying for Graphite, or if you just like running your own stuff — give it a spin.
And if GitHub goes down while you're reading this... well. You know what to do.
Built with TypeScript, Astro, frustration, and an embarrassing amount of coffee. PRs welcome.
— Swadhin
Tags: #selfhosted #git #opensource #devtools #typescript #astro
#github-alternative #stacked-prs