Shipyard is a Kubernetes-native multi-tenant deployment platform with a React frontend, OAuth authentication, RBAC, tiered subscriptions, and secrets management.
System Architecture
The platform consists of three main components working together:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ CLIENTS │
│ (Web Browser / API) │
└─────────────────────────────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ REACT SPA (ui/) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ React 19 │ │ React │ │ TanStack │ │ React Router 7 │ │
│ │ + Vite │ │ Hook Form │ │ Query │ │ (Client-side routing) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ Pages │ │
│ │ Dashboard │ Teams │ Secrets │ Billing │ Templates │ Deployments │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────┬───────────────────────────────────────────┘
│ HTTP API
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ GO SERVER (cmd/server) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ OAuth │ │ Session │ │ Auth │ │ Embedded SPA │ │
│ │ (Google) │ │ Store │ │ Middleware │ │ (go:embed ui/dist) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ REST API Handlers │ │
│ │ /api/deployments /api/secrets /api/templates /api/orgs /api/teams │ │
│ │ /api/billing /api/clients /api/me /api/invites │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ Middleware Stack │ │
│ │ Security Headers → Auth → RBAC (Authz) → Audit Logging → Handler │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
└────────────┬─────────────────────┬─────────────────────┬────────────────────────┘
│ │ │
▼ ▼ ▼
┌────────────────────┐ ┌──────────────────┐ ┌─────────────────────────────────┐
│ PostgreSQL │ │ Vault/AWS │ │ Kubernetes API │
│ (db.Store) │ │ (secrets.Store) │ │ (controller-runtime client) │
│ │ │ │ │ │
│ - Users │ │ - User secrets │ │ Creates UserDeployment CRs │
│ - Organizations │ │ - Template │ │ │
│ - Teams │ │ secrets │ └────────────────┬────────────────┘
│ - Deployments │ │ - Deployment │ │
│ - Templates │ │ secrets │ │
│ - Billing Plans │ │ - Per-service │ │
│ - Clients │ │ secrets │ │
│ - Sessions │ │ │ │
└────────────────────┘ └──────────────────┘ │
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ OPERATOR (cmd/operator) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ UserDeployment Controller │ │
│ │ │ │
│ │ Watches: UserDeployment CRs │ │
│ │ Creates: Deployments, Services, Ingresses, ConfigMaps, │ │
│ │ ExternalSecrets, NetworkPolicies │ │
│ │ Features: Health probes, resource limits, observability │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────┬────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ KUBERNETES RESOURCES │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Deployment │ │ Service │ │ Ingress │ │ ExternalSecret │ │
│ │ (per svc) │ │ (per svc) │ │ (per svc) │ │ (syncs from Vault) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────────────────┐ │
│ │ ConfigMap │ │ K8s Secret │ │ NetworkPolicy │ │
│ │ (user data) │ │ (from ESO) │ │ (tier-based isolation) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
Core Components
1. React Frontend
Modern single-page application built with:
React 19 with TypeScript
Vite 7 for fast development and optimized builds
TanStack Query 5 for server state management
React Router 7 for client-side routing
React Hook Form + Zod for form validation
Tailwind CSS 4 for styling
Recharts for metrics visualization
Key features:
Dashboard with deployment cards
Team management (create, invite, manage members)
Secrets management (three-tier: user/template/deployment)
Template creation with YAML spec editor
Real-time deployment logs and metrics
Billing and subscription management
Location: k8s-scheduler/ui/
2. Go Server
REST API backend serving both the API and embedded React application:
OAuth Authentication : Google OAuth2 flow
Session Management : Memory/PostgreSQL/Redis backends
Embedded SPA : React app served via go:embed
RBAC Middleware : Permission checks on all endpoints
Template Management : CRUD with visibility scopes
Deployment Lifecycle : Create/delete/monitor deployments
Secrets Management : Three-tier secrets architecture
Billing Integration : Stripe subscriptions and webhooks
Location: k8s-scheduler/cmd/server/
Key API endpoints:
Authentication
Deployments
Organizations & Teams
# Initiate Google OAuth
GET /oauth/google
# OAuth callback
GET /oauth/google/callback
# End session
POST /logout
3. Kubernetes Operator
Kubernetes operator built with controller-runtime:
Watches : UserDeployment custom resources
Reconciles : Creates/updates/deletes K8s resources
Manages : Deployments, Services, Ingresses, ConfigMaps, ExternalSecrets
Features : Health probes, resource limits, Prometheus metrics
Location: k8s-scheduler/cmd/operator/
Custom Resource Definition:
k8s-scheduler/manifests/userdeployment-crd.yaml
apiVersion : apiextensions.k8s.io/v1
kind : CustomResourceDefinition
metadata :
name : userdeployments.scheduler.shipyard.sh
spec :
group : scheduler.shipyard.sh
versions :
- name : v1alpha1
served : true
storage : true
Data Flow
Understanding how a deployment is created:
User Action
User clicks Create Deployment in the React UI, fills out the form with template selection and configuration.
API Request
React app sends POST request to /api/deployments with deployment configuration.
Server Validation
Go server validates the request, checks RBAC permissions, and verifies the user has quota available for their subscription tier.
Create Custom Resource
Server creates a UserDeployment custom resource in Kubernetes: internal/server/server.go
ud := & v1alpha1 . UserDeployment {
ObjectMeta : metav1 . ObjectMeta {
Name : deployment . Name ,
Namespace : fmt . Sprintf ( "sandbox- %s " , userID ),
},
Spec : v1alpha1 . UserDeploymentSpec {
UserID : userID ,
Template : deployment . Template ,
Services : deployment . Services ,
// ...
},
}
kubeClient . Create ( ctx , ud )
Operator Watches
Operator’s controller watches for new UserDeployment resources and triggers reconciliation loop.
Reconcile Loop
Operator creates all necessary Kubernetes resources:
Deployments (one per service in the template)
Services (for internal networking)
Ingresses (for external access)
ExternalSecrets (to sync secrets from Vault)
ConfigMaps (for user configuration)
NetworkPolicies (for tenant isolation)
Pods Running
Kubernetes schedules pods, pulls images, mounts secrets, and starts containers.
Status Updates
Operator updates the UserDeployment status field with pod health, URLs, and any errors. React UI polls for updates and displays deployment status.
Multi-Tenancy Model
Shipyard implements a hierarchical multi-tenancy model:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ PLATFORM (Your Instance) │
└─────────────────────────────────────┬───────────────────────────────────────────┘
│
┌─────────────────────────────┼─────────────────────────────┐
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ Client A │ │ Client B │ │ Direct Org │
│ (White-label) │ │ (White-label) │ │ (No client) │
│ custom domain │ │ custom domain │ │ │
│ own branding │ │ own branding │ │ │
│ billing plans │ │ billing plans │ │ │
└─────────┬─────────┘ └─────────┬─────────┘ └─────────┬─────────┘
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ │
▼ ▼ ▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────────┐
│ Org 1 │ │ Org 2 │ │ Org 3 │ │ Org 4 │ │ Org 5 │
└───┬───┘ └───────┘ └───────┘ └───────┘ └─────┬─────┘
│ │
├── Team A (Backend) ├── Team X
│ ├── [email protected] (developer) └── Team Y
│ └── [email protected] (team_admin)
│
└── Team B (Frontend)
└── [email protected] (viewer)
RBAC Hierarchy
Every user action is validated against their role at the organization and team level.
Level Roles Permissions Organization org_ownerFull org control, billing, all teams org_adminManage teams, templates, members org_memberAccess assigned teams only Team team_adminManage team members, deployments, invites developerCreate/manage deployments, view secrets viewerRead-only access to deployments
Implementation: k8s-scheduler/internal/authz/authorizer.go
Secrets Architecture
Three-tier secrets system for flexible secret management:
┌─────────────────────────────────────────────────────────────────┐
│ VAULT │
│ │
│ users/{userId}/ │
│ ├── secrets/ # User-level (global) secrets │
│ ├── templates/{name}/ # Template secrets │
│ │ └── services/{svc}/ # Per-service template secrets │
│ └── deployments/{name}/ # Deployment-specific secrets │
│ └── services/{svc}/ # Per-service deployment secrets │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ EXTERNAL SECRETS OPERATOR │
│ │
│ ExternalSecret CR ──syncs──▶ K8s Secret │
│ (references Vault path) (mounted in pods) │
└─────────────────────────────────────────────────────────────────┘
Secret Levels
Level Path Lifecycle Use Case User users/{id}/secretsPersistent API keys shared across deployments Template users/{id}/templates/{name}Persistent Template-specific config Service .../services/{svc}Persistent Per-container secrets Deployment users/{id}/deployments/{name}Ephemeral Deployment-specific overrides
Implementation: k8s-scheduler/internal/secrets/vault.go
Infrastructure Components
Shipyard consists of three main repository components:
1. k8s-scheduler
Main application code (React frontend + Go backend + Kubernetes operator)
Tech stack:
Go 1.24 with standard library HTTP server
PostgreSQL (pgx/v5) for data persistence
controller-runtime for Kubernetes operator
React 19 + Vite 7 + TailwindCSS 4
Reusable Terraform modules for AWS infrastructure:
VPC : Virtual private cloud with public/private subnets
EKS : Amazon EKS cluster with managed node groups
RDS : PostgreSQL database
Vault : HashiCorp Vault infrastructure (KMS, DynamoDB)
EC2 : Bastion hosts and utility instances
State : S3 + DynamoDB for Terraform state
Location: terraform-modules/
3. infra
Environment-specific infrastructure deployments using the Terraform modules.
Three-layer architecture:
Infrastructure Layer : VPC, EKS, Tailscale VPN, Vault
Platform Layer : ALB Controller, cert-manager, External Secrets Operator
Apps Layer : ArgoCD, Traefik, application TLS certificates
Location: infra/terraform/dev/
Subscription Tiers
Feature availability varies by subscription tier:
Feature Free Business Enterprise Deployments 1 5 Unlimited Team Members 1 10 Unlimited Templates System only + Custom + Org-wide Network Isolation Shared Namespace Dedicated Support Community Email Dedicated
Enforcement: k8s-scheduler/internal/tierlimits/
Next Steps
Quick Start Get Shipyard running locally in minutes
K8s Scheduler Multi-tenant deployment platform
Terraform Modules Reusable infrastructure modules
Infrastructure Guide Deploy the full stack on AWS