Skip to content

Repository files navigation

Relay

Developer cloud for shipping apps — deploy from Git (or a fixture), manage env vars, stream logs, watch metrics, attach Postgres/Redis, roll back, and collaborate with your team. One dashboard.

Product UI brand: Relay · Monorepo / package names: nexus_* (historical)


Why this exists

Relay is a self-hosted PaaS-style demo: the control plane you’d expect from platforms like Railway or Render, built end-to-end so you can run builds, deploys, and ops tooling locally with Docker.

git push / Deploy  →  build image  →  run container  →  live URL + logs + metrics

Features

Area What you get
Auth & orgs JWT login, organization membership, roles
Projects & services CRUD for projects; services with Git or fixture:// sources
Deploy pipeline Docker build → deploy (Docker or Kubernetes via DEPLOY_MODE)
Revisions Deployment history, one-click rollback to a previous live image
Environment Per-service env vars applied on next deploy
Logs Live WebSocket log stream (container tail + follow)
Metrics Live CPU / memory / network / block I/O / PIDs via Docker stats
Add-ons One-click Postgres / Redis; connection URL injected into linked services
Domains Custom domain records on a service
Team Invite / remove members, audit activity feed
Billing Usage-style cost dashboard for the org
Notifications In-app deploy notifications
Webhooks GitHub push → enqueue deploy; optional outgoing project webhook

Architecture

System overview

flowchart TB
  subgraph Client
    WEB[React + Vite dashboard<br/>:5173]
  end

  subgraph Platform["docker compose"]
    API[FastAPI<br/>:8000]
    WRK[ARQ worker]
    PG[(PostgreSQL)]
    RD[(Redis)]
  end

  subgraph Runtime
    DOC[Docker Engine<br/>or Kubernetes]
    APP[App containers<br/>e.g. nexus-hello-api]
  end

  WEB -->|REST + WebSocket| API
  API --> PG
  API -->|enqueue jobs| RD
  WRK --> RD
  WRK --> PG
  WRK -->|build / run / stats / logs| DOC
  DOC --> APP
  API -->|docker sock: logs + metrics| DOC
Loading

Deploy flow

sequenceDiagram
  actor User
  participant UI as Dashboard
  participant API as FastAPI
  participant Q as Redis / ARQ
  participant W as Worker
  participant D as Docker / K8s

  User->>UI: Click Deploy
  UI->>API: POST /deployments/by-service/{id}
  API->>API: Create deployment (queued)
  API->>Q: enqueue_deploy(deployment_id)
  API-->>UI: Deployment row
  Q->>W: Job picked up
  W->>W: Clone / copy fixture
  W->>D: docker build
  W->>D: docker run / k8s apply
  W->>API: Status → live + URL
  UI->>API: Poll deployments / open URL
  User->>UI: Logs tab (WebSocket)
  UI->>API: WS logs stream
  API->>D: docker logs -f
Loading

Data model (simplified)

erDiagram
  User ||--o{ Membership : has
  Organization ||--o{ Membership : has
  Organization ||--o{ Project : owns
  Project ||--o{ Service : contains
  Project ||--o{ Addon : has
  Service ||--o{ Deployment : ships
  Service ||--o{ ServiceEnvVar : configures
  Service ||--o{ CustomDomain : maps
  Addon ||--o{ AddonLink : links
  Service ||--o{ AddonLink : consumes
Loading

Tech stack

Layer Choice
API Python 3.12, FastAPI, SQLAlchemy, Alembic, JWT
Worker ARQ (async Redis queue)
Core lib packages/nexus_core (models, deploy, build, logs, metrics, billing)
Dashboard React, TypeScript, Vite, React Router, Lucide icons
Data PostgreSQL 16, Redis 7
Runtime Docker (default / auto-fallback); optional Kubernetes (kind)
Fixtures fixtures/hello-api for zero-Git deploys

Repository layout

.
├── apps/
│   ├── api/          # FastAPI app + entrypoint (migrate + seed + uvicorn)
│   ├── worker/       # ARQ worker (builds & deploys)
│   └── web/          # React dashboard
├── packages/
│   └── nexus_core/   # Shared domain: models, deploy, logs, metrics, seed
├── fixtures/
│   └── hello-api/    # Sample app (fixture://hello-api)
├── scripts/
│   └── kind-up.sh    # Optional local Kubernetes
└── docker-compose.yml

Prerequisites

  • Docker Desktop (or Docker Engine) with Compose v2
  • Ports free: 5173 (web), 8000 (API), 5432, 6379
  • Optional: kind if you want Kubernetes deploys (./scripts/kind-up.sh)

Quick start

# From the repo root
docker compose up --build
Service URL
Dashboard http://localhost:5173
API docs http://localhost:8000/docs
Health http://localhost:8000/health

Demo login

Email:    demo@relay.dev
Password: relay-demo

Seed data includes a demo org, several projects (e.g. Demo Project, Acme Frontend), and Hello API ready to deploy.

First deploy (2 minutes)

  1. Sign in with the demo credentials.
  2. Open Demo ProjectHello API.
  3. Click Deploy.
  4. Wait until status is live and a http://localhost:… URL appears.
  5. Open Logs and Metrics — stream and live Docker stats.

Source for Hello API: fixture://hello-api (copied from fixtures/hello-api, no Git required).


How deploys work

  1. API creates a Deployment row and enqueues an ARQ job.
  2. Worker resolves the source:
    • fixture://name → copy from FIXTURES_DIR
    • otherwise → git clone --depth 1
  3. docker build produces an image.
  4. Runtime depends on DEPLOY_MODE:
Mode Behavior
docker Run container nexus-{service-slug}, publish host port
kubernetes Deploy into cluster (expects kubeconfig / kind)
auto (default) Prefer Kubernetes; fall back to Docker on failure
  1. Deployment marked live with public URL; env vars and linked add-on URLs are injected at run time.

Dashboard map

Projects
  └─ Project detail
       ├─ Services list (+ create)
       └─ Add-ons (Postgres / Redis)
            └─ Service detail
                 ├─ Deployments (+ rollback)
                 ├─ Environment
                 ├─ Logs (live WS)
                 ├─ Metrics (poll ~3s)
                 └─ Domains

Organization
  ├─ Team (members + activity)
  └─ Billing (usage)

API surface (high level)

Prefix Purpose
/auth Login
/me Current user
/orgs Orgs, members, activity, usage
/projects Projects + project webhook
/services Services, env, metrics, log tail, domains
/deployments List / create / rollback + logs WebSocket
/addons Create, link, delete add-ons
/notifications In-app notifications
/webhooks Inbound GitHub (and related) hooks
/health Liveness

Interactive OpenAPI: http://localhost:8000/docs

Live logs

WS  ws://localhost:8000/deployments/by-service/{service_id}/logs/ws?token=<jwt>

On connect: historical tail (or build log fallback) → then follow new lines.


Configuration

Set via environment (see docker-compose.yml and nexus_core.config.Settings):

Variable Default / notes
DATABASE_URL Postgres DSN
REDIS_URL Redis for ARQ
JWT_SECRET Change in any shared environment
CORS_ORIGINS e.g. http://localhost:5173
DEPLOY_MODE auto | docker | kubernetes
FIXTURES_DIR Worker path to fixtures (compose: /fixtures)
REGISTRY_URL Image registry when using K8s path
VITE_API_URL Browser → API base (http://localhost:8000)
WEBHOOK_SECRET Optional GitHub webhook HMAC

Local commands

# Start / rebuild everything
docker compose up --build

# Rebuild only the API (after core/seed changes)
docker compose up --build api -d

# Rebuild only the dashboard
docker compose up --build web -d

# Logs
docker compose logs -f api worker web

# Tear down (keep DB volume)
docker compose down

# Tear down + wipe DB
docker compose down -v

Optional Kubernetes bootstrap:

./scripts/kind-up.sh
# then set DEPLOY_MODE=kubernetes (or keep auto)
docker compose up --build

Demo seed

On API boot, Alembic migrates and seed_demo_data ensures:

  • User demo@relay.dev / relay-demo
  • Demo organization + ownership
  • Demo Project → Hello API (fixture://hello-api)
  • Extra sample projects (Acme, Payment, Analytics) with idle services

Security notes

  • Default JWT secret and DB passwords are for local demo only.
  • The API and worker mount the Docker socket so they can build/run/stats/logs — treat that as full host Docker access.
  • Do not expose this stack to the public internet without hardening auth, secrets, and socket access.

Roadmap status

Phase Scope Status
0 Auth, org / project / service CRUD Done
1 Fixture/Git → Docker build → deploy Done
2 Env vars, live logs, rollbacks Done
3 Postgres/Redis add-ons + URL inject Done
4 Metrics, custom domains Done
5 Team + audit activity Done
6 GitHub webhooks, notifications, billing UI Done

Interview / portfolio talking points

Useful angles when explaining Relay:

  1. Control plane vs data plane — API/worker/DB vs customer containers.
  2. Async jobs — HTTP stays fast; builds run on ARQ workers.
  3. Deploy modes — Docker for local demos; K8s path for “real” clustering.
  4. Observability — WebSocket logs + Docker stats without a separate APM stack.
  5. Multi-tenant sketch — orgs, memberships, project isolation by slug/namespace naming.

License

No license file in-repo yet — treat as private / portfolio unless you add one.


Development & Commit History Note

Note

This project was developed locally and subsequently backfilled with a complete daily commit history spanning from March 21, 2026 (project initiation) through March 27, 2026 (final completion and release).

About

A self-hosted PaaS control plane to build, deploy, and manage web services, background workers, and add-ons using Docker & K8s.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages