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)
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
| 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 |
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
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
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
| 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 |
.
├── 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
- 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)
# 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 |
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.
- Sign in with the demo credentials.
- Open Demo Project → Hello API.
- Click Deploy.
- Wait until status is live and a
http://localhost:…URL appears. - Open Logs and Metrics — stream and live Docker stats.
Source for Hello API: fixture://hello-api (copied from fixtures/hello-api, no Git required).
- API creates a
Deploymentrow and enqueues an ARQ job. - Worker resolves the source:
fixture://name→ copy fromFIXTURES_DIR- otherwise →
git clone --depth 1
docker buildproduces an image.- 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 |
- Deployment marked live with public URL; env vars and linked add-on URLs are injected at run time.
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)
| 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
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.
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 |
# 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 -vOptional Kubernetes bootstrap:
./scripts/kind-up.sh
# then set DEPLOY_MODE=kubernetes (or keep auto)
docker compose up --buildOn 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
- 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.
| 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 |
Useful angles when explaining Relay:
- Control plane vs data plane — API/worker/DB vs customer containers.
- Async jobs — HTTP stays fast; builds run on ARQ workers.
- Deploy modes — Docker for local demos; K8s path for “real” clustering.
- Observability — WebSocket logs + Docker stats without a separate APM stack.
- Multi-tenant sketch — orgs, memberships, project isolation by slug/namespace naming.
No license file in-repo yet — treat as private / portfolio unless you add one.
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).