DEV Community

Alex A. Gromov
Alex A. Gromov

Posted on

I built a GitLab alternative that runs on 100 MB RAM

I got tired of GitLab eating 4+ GB RAM on my homelab server. So I built GitRiver - a self-hosted Git platform as a single Rust binary with built-in CI/CD, container registry, package registry, and even a GitOps deployer for Kubernetes.

What's inside (all in one binary)

  • Git hosting - HTTP + SSH, LFS, GPG signing, web editor
  • Pull Requests - code review, CODEOWNERS, merge queue, comment threads
  • CI/CD - YAML pipelines, DAG, matrix builds, artifacts, caching, web terminal
  • Container Registry - OCI v2, multi-arch, retention policies, garbage collection
  • Package Registry - npm, PyPI, Cargo, Maven, NuGet, Generic
  • GitOps Deploy (RiverCD) - built-in K8s deployer with canary/blue-green, drift detection, sync waves
  • Issues - Kanban boards, milestones, templates
  • 8 notification channels - Email, Telegram, Slack, Discord, Teams, Matrix, Webhook, In-app
  • Backup & Restore - AES-256-GCM encryption, incremental, S3 streaming
  • Wiki, Pages, Releases, Import/Mirroring

Quick start

Create .env:

DB_USER=gitriver
DB_PASS=changeme
DB_NAME=gitriver
Enter fullscreen mode Exit fullscreen mode

Create docker-compose.yml:

services:
  postgres:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASS}
      POSTGRES_DB: ${DB_NAME}
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
      interval: 5s
      retries: 5

  gitriver:
    image: gitriver/gitriver:latest
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    ports:
      - "3000:3000"
    environment:
      GITRIVER_DB_HOST: postgres
      GITRIVER_DB_PORT: 5432
      GITRIVER_DB_USER: ${DB_USER}
      GITRIVER_DB_PASS: ${DB_PASS}
      GITRIVER_DB_NAME: ${DB_NAME}
    volumes:
      - ./data/gitriver:/var/lib/gitriver
      - /var/run/docker.sock:/var/run/docker.sock
Enter fullscreen mode Exit fullscreen mode
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:3000 - the setup wizard will create an administrator.

How is it different from Gitea?

Gitea is an excellent project - it has Actions (CI/CD), container registry, and even more package formats than GitRiver. If you need a lightweight Git server with CI, Gitea is a solid choice.

GitRiver focuses on features Gitea doesn't have:

  • Built-in GitOps Deploy (RiverCD) - deploy to Kubernetes directly from the UI, with canary/blue-green strategies, drift detection, sync waves. No need for ArgoCD or Flux.
  • Merge Queue - automatic CI-verified merging with temporary branches
  • DORA Metrics - deployment frequency, lead time, MTTR, change failure rate
  • SCIM 2.0 - automatic user/group provisioning from IdP

Why not GitLab?

GitLab requires 4+ GB RAM, Redis, Sidekiq, Puma, and dozens of processes. GitRiver runs on ~100 MB RAM with a single process + PostgreSQL.

Tech stack

  • Backend: Rust (Axum, SQLx, Tokio) - 16 crates, hexagonal architecture
  • Frontend: React 19 + TypeScript + Tailwind CSS
  • Database: PostgreSQL 16+
  • API: 400+ REST endpoints with OpenAPI 3.1

Community edition is free

No limits on users, repos, CI pipelines, registries. Pro adds SAML/SCIM SSO, DORA metrics, audit log, custom roles.

🌐 Website | 🐳 Docker Hub | 📖 Docs

Happy to answer any questions!

Top comments (0)