Spin up a full MERN, React, or Go + PostgreSQL development environment with a single command — isolated, ephemeral, and auto-destroyed after a TTL.
devctl up --template=mern
# ✅ Environment is ready!
# URL: http://abhay-mern.dev.local
# Namespace: dev-abhay
# TTL: 30mEvery developer has been here:
| Pain Point | Reality |
|---|---|
| "It works on my machine" | Environment drift between teammates causes hours of debugging |
| Slow onboarding | A new engineer spends a full day getting their local stack running |
| Version conflicts | Multiple projects need conflicting Node/Python/Go versions simultaneously |
| CI ≠ Local | Bugs only appear in production because CI environments don't match local |
| Leftover containers | Forgotten Docker containers consume RAM and cause port conflicts |
devctl solves this. Every developer gets a real, isolated Kubernetes namespace with all services wired together — in under 60 seconds. When you're done, devctl down destroys everything cleanly. No leftover containers, no port conflicts, no config drift.
devctl up --template=mern
│
│ POST /api/v1/environments {template, username, ttl}
▼
Backend API (202 Accepted immediately)
├── CreateNamespace → dev-abhay (labels + TTL annotation)
├── ApplyResourceQuota → CPU/memory limits
├── ApplyPVC → mongodb-data (1Gi)
├── ApplyConfigMap → express-config, react-nginx-config
├── ApplyDeployment → mongodb, express, react (with health probes)
└── ApplyIngress → abhay-mern.dev.local → express:3000
CLI polls GET /environments/abhay-mern/status every 3s
└── Aggregates pod phases → Ready / Provisioning / Error
┌──────────────────────────────────────────────────────────────────┐
│ Developer Laptop │
│ │
│ ┌─────────────┐ HTTP/REST ┌──────────────────────────────┐ │
│ │ devctl CLI │──────────────▶│ devctl Backend (Go + Chi) │ │
│ │ (Go/Cobra) │ │ POST /api/v1/environments │ │
│ └─────────────┘ │ GET /api/v1/templates │ │
│ └──────────────┬───────────────┘ │
│ │ client-go │
│ ▼ │
│ ┌──────── Kind Cluster ────────────────┐ │
│ │ │ │
│ │ namespace: devctl-system │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ devctl-backend (Deployment) │ │ │
│ │ │ ClusterRole + RBAC │ │ │
│ │ │ CronJob: TTL cleanup/5min │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ │ namespace: dev-<username> │ │
│ │ ┌──────────┐ ┌─────────┐ ┌──────┐ │ │
│ │ │ mongodb │ │ express │ │react │ │ │
│ │ │ PVC: 1Gi │ │ /health │ │ / │ │ │
│ │ └──────────┘ └────┬────┘ └──┬───┘ │ │
│ │ ResourceQuota (CPU/Mem) │ │
│ │ Ingress → <user>-mern.dev.local │
│ └──────────────────────────────────────┘ │
│ │
│ ┌────────────────────┐ Helm ┌────────────────────────────┐ │
│ │ Terraform (Kind) │──────────▶│ ingress-nginx Controller │ │
│ └────────────────────┘ └────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
| Template | Services | Ports | Persistent Storage |
|---|---|---|---|
mern |
MongoDB 7, Express (Node 20), React (nginx) | 27017, 3000, 80 | MongoDB: 1Gi PVC |
react |
React (nginx 1.25) | 80 | — |
go-api |
Go API, PostgreSQL 16 | 8080, 5432 | Postgres: 1Gi PVC |
All templates include namespace isolation, health probes, TTL-based auto-cleanup, and ingress routing.
devctl list-templates
# NAME VERSION SERVICES
# go-api 1.0.0 api, postgres
# mern 1.0.0 mongodb, express, react
# react 1.0.0 reactdev-env-platform/
│
├── cli/ # devctl binary (Go + Cobra)
│ ├── cmd/
│ │ ├── root.go # Global flags, Viper config, env bindings
│ │ ├── up.go # devctl up — creates + polls for ready
│ │ ├── down.go # devctl down — deletes namespace
│ │ ├── status.go # devctl status [--watch]
│ │ ├── list_templates.go # devctl list-templates [--output=json]
│ │ ├── version.go # devctl version
│ │ ├── up_test.go # Tests: validateTemplate, getEnvStatus
│ │ └── down_test.go # Tests: runDown against mock HTTP server
│ ├── Dockerfile # Multi-stage Alpine build
│ ├── Makefile
│ └── go.mod
│
├── backend/ # REST API server (Go + Chi)
│ ├── main.go # Entrypoint: init K8s client, start server
│ ├── internal/
│ │ ├── api/
│ │ │ ├── server.go # Chi router, middleware wiring
│ │ │ ├── handlers/
│ │ │ │ ├── environments.go # POST/DELETE/GET + K8sBackend interface
│ │ │ │ ├── templates.go # GET /templates
│ │ │ │ ├── environments_test.go
│ │ │ │ ├── templates_test.go
│ │ │ │ └── mock_k8s_test.go # Mock K8s client for unit tests
│ │ │ └── middleware/
│ │ │ ├── auth.go # Bearer token validation
│ │ │ └── logging.go # Request logging
│ │ ├── k8s/
│ │ │ ├── client.go # In-cluster / kubeconfig fallback
│ │ │ ├── namespace.go # Create/Delete/List namespaces
│ │ │ ├── manifests.go # ApplyDeployment/Service/ConfigMap/PVC/Ingress
│ │ │ ├── status.go # Pod phase aggregation → env phase
│ │ │ └── ingress.go # Extract URL from Ingress rules
│ │ ├── templates/
│ │ │ ├── loader.go # Embedded FS, ListTemplates, TemplateExists
│ │ │ ├── renderer.go # Go text/template rendering, BuildVars
│ │ │ └── templates/ # Embedded template manifests
│ │ │ ├── mern/ # MongoDB + Express + React stack
│ │ │ ├── react/ # Standalone React (nginx)
│ │ │ └── go-api/ # Go API + PostgreSQL
│ │ └── models/
│ │ ├── environment.go # Status enums, request/response types
│ │ └── template.go # Template, Service structs
│ ├── Dockerfile
│ ├── Makefile
│ └── go.mod
│
├── k8s-manifests/
│ ├── backend/
│ │ ├── deployment.yaml # devctl-backend: 1 replica, health probes
│ │ ├── service.yaml # ClusterIP :8080
│ │ ├── clusterrole.yaml # Full RBAC for namespaces/pods/deployments
│ │ ├── clusterrolebinding.yaml
│ │ └── serviceaccount.yaml
│ └── system/
│ └── cronjob-cleanup.yaml # Every 5min: delete namespaces past TTL
│
├── terraform/
│ ├── main.tf # Kind provider + ingress-nginx Helm release
│ ├── variables.tf
│ ├── outputs.tf
│ └── modules/kind-cluster/ # Reusable Kind cluster module
│
├── scripts/
│ ├── setup.sh # Full bootstrap: terraform → deploy backend
│ ├── teardown.sh # Full cleanup: namespaces → terraform destroy
│ ├── install-devctl.sh # Build + install CLI to /usr/local/bin
│ ├── dev.sh # Hot-reload backend locally
│ └── integration-test.sh # 9-step E2E: create → verify → status → destroy
│
├── docs/
│ ├── architecture.png # Architecture diagram (1024×1024)
│ ├── demo.html # Animated terminal demo — open in browser
│ ├── STRUCTURE.md # Full annotated folder tree + design decisions
│ └── DEPLOYMENT.md # Step-by-step deployment guide (3 options)
│
├── .github/workflows/
│ ├── ci.yml # Lint, test (race), validate K8s YAML, build
│ └── release.yml # GoReleaser + Docker push to GHCR on tag
│
├── .goreleaser.yaml # Multi-platform CLI builds (Linux/macOS/Windows)
├── docker-compose.yml # Backend + CLI for local dev (no Kind needed)
└── README.md
| Tool | Version | Install |
|---|---|---|
| Go | 1.23+ | golang.org/dl |
| Docker | 20+ | docs.docker.com |
| kubectl | 1.28+ | kubernetes.io/docs |
| Terraform | 1.5+ | developer.hashicorp.com |
| kind | 0.22+ | Installed automatically by Terraform |
Full guide with troubleshooting: docs/DEPLOYMENT.md
1. Clone and bootstrap the cluster
git clone https://github.com/abhaychaurasiya/dev-env-platform.git
cd dev-env-platform
./scripts/setup.shThis script runs 5 steps automatically:
- Checks prerequisites (terraform, kubectl, docker)
- Provisions a Kind cluster via
terraform apply - Installs ingress-nginx via Helm
- Deploys the devctl backend + RBAC into
devctl-system - Deploys the TTL CronJob
2. Install the CLI
./scripts/install-devctl.sh
devctl version
# devctl v0.1.03. Forward the backend port (separate terminal)
kubectl port-forward -n devctl-system svc/devctl-backend 8080:80804. Create your first environment
devctl up --template=mern
# Add to /etc/hosts so the URL works:
echo "127.0.0.1 $(whoami)-mern.dev.local" | sudo tee -a /etc/hosts
# Open in browser:
open http://$(whoami)-mern.dev.local5. Tear down everything
./scripts/teardown.sh
# Deletes all dev namespaces → removes devctl-system → terraform destroyRuns the backend using your existing kubeconfig. Useful for testing the API against an existing cluster.
# Spin up the backend
docker compose up -d
# Verify it's healthy
curl http://localhost:8080/healthz
# {"status":"ok"}
curl http://localhost:8080/api/v1/templates
# [{"name":"go-api",...},{"name":"mern",...},{"name":"react",...}]
# Run the CLI as a one-shot container
docker compose --profile cli run cli up --template=react# Terminal 1 — backend with live reload
./scripts/dev.sh
# Terminal 2 — CLI against local backend
export DEVCTL_BACKEND_URL=http://localhost:8080
devctl up --template=reactdevctl up Spin up a dev environment
devctl down Tear down a dev environment
devctl status Show environment status
devctl list-templates List available templates
devctl version Print version
| Flag | Command | Default | Description |
|---|---|---|---|
--template / -t |
up |
(required) | Template: mern, react, go-api |
--timeout |
up |
30m |
Environment TTL (e.g. 1h, 2h) |
--username |
up |
$USER |
Namespace isolation key |
--name |
down, status |
dev-$USER |
Environment to target |
--watch / -w |
status |
false |
Refresh every 5 seconds |
--output |
list-templates |
table |
table or json |
--backend-url |
all | http://localhost:8080 |
Backend URL (env: DEVCTL_BACKEND_URL) |
--token |
all | — | Auth token (env: DEVCTL_TOKEN) |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/v1/environments |
Create environment → 202 Accepted |
DELETE |
/api/v1/environments/{name} |
Destroy environment → 204 No Content |
GET |
/api/v1/environments/{name}/status |
Get phase + service statuses → 200 |
GET |
/api/v1/templates |
List available templates → 200 |
GET |
/healthz |
Health check → 200 |
# Create
curl -X POST http://localhost:8080/api/v1/environments \
-H "Content-Type: application/json" \
-d '{"template":"mern","username":"abhay","ttl":"1h"}'
# Status
curl http://localhost:8080/api/v1/environments/abhay-mern/status# Backend unit tests (no cluster needed)
cd backend && make test
# CLI unit tests
cd cli && make test
# Full E2E integration test (requires running cluster + backend)
DEVCTL_BACKEND_URL=http://localhost:8080 ./scripts/integration-test.shThe integration test covers 9 steps: health check → list templates → create → verify namespace → check ResourceQuota → wait for Ready → validate status → teardown → verify deletion.
Environments are tagged with an RFC3339 expiry annotation. A CronJob runs every 5 minutes and deletes any namespace past its TTL automatically.
devctl up --template=mern # Default TTL: 30m
devctl up --template=mern --timeout=2h # Custom TTL
devctl down # Manual teardown anytime- CLI —
up,down,status,list-templates,version - Backend REST API with Kubernetes
client-go - Namespace isolation + ResourceQuotas
- Health probes (liveness + readiness) on all containers
- PVC-backed storage for MongoDB and PostgreSQL
- TTL-based auto cleanup via CronJob
- Templates: MERN, React, Go+PostgreSQL
- GitHub Actions CI + GoReleaser + Docker release pipeline
- Unit tests (backend handlers + CLI commands)
- Web UI dashboard
- Multi-cloud support (EKS, GKE)
- Secrets management (Vault integration)
- Logging & monitoring (Loki + Grafana)
MIT © Abhay Chaurasiya
