@jsleekr/depstart
v0.1.0
Published
<div align="center">
Downloads
63
Readme
🚀 depstart
Start services in the right order
Local dev service orchestrator with dependency ordering and health checks.
Dependency DAG + Parallel Startup + Health Checks
Why This Exists
Starting a local development stack by hand means memorizing which services depend on which, waiting for databases to be ready before launching APIs, and manually restarting anything that crashes. depstart reads a simple YAML config and handles all of it -- services launch in the correct topological order, health checks gate each layer, and crashes trigger automatic restarts with exponential backoff. One command replaces a morning ritual of terminal tabs.
Features
| Feature | Description | |---------|-------------| | Dependency DAG | Topological sort ensures correct start order | | Parallel Startup | Independent services launch simultaneously | | Health Checks | TCP, HTTP, and command-based readiness probes | | Colored Logs | Multiplexed output with per-service prefixes | | Auto-Restart | Exponential backoff with configurable policies | | Graceful Shutdown | SIGTERM -> SIGKILL cascade (Windows: taskkill) | | Config Validation | Fail-fast with actionable error messages | | Programmatic API | Import and use from TypeScript/JavaScript | | Cross-Platform | Works on macOS, Linux, and Windows |
depstart.yaml depstart up
+------------+ +---------+---------+
| postgres | => | Layer 0 | pg, redis (parallel)
| redis | +---------+---------+
| api -> [pg,redis] | Layer 1 | api
| web -> [api]| +---------+---------+
+------------+ | Layer 2 | web
+-------------------+Requirements
- Node.js >= 18 (uses ES2022 features)
- npm >= 9
Quick Start
# Install globally
npm install -g depstart
# Create a config in your project
depstart init # generates depstart.yaml with example services
# Validate your config (optional)
depstart validate # checks YAML syntax, dependencies, health checks
# Start all services
depstart up # starts in dependency order, shows colored logs
# Stop everything
# Press Ctrl+C in the terminal running depstart upUsage
depstart init # Generate sample depstart.yaml
depstart up # Start services in dependency order
depstart up -c custom.yaml # Use a custom config file
depstart status # Show service configuration
depstart validate # Validate config without starting
depstart down # Stop all services (use Ctrl+C on running instance)
depstart logs [service] # View service logs (shown in real-time during `up`)Configuration
Create a depstart.yaml in your project root:
services:
postgres:
cmd: "docker run -p 5432:5432 -e POSTGRES_PASSWORD=secret postgres:16"
health:
tcp: "localhost:5432"
timeout: 10s
redis:
cmd: "redis-server"
health:
tcp: "localhost:6379"
api:
cmd: "npm run dev"
depends_on: [postgres, redis]
health:
http: "http://localhost:8080/health"
env:
DATABASE_URL: "postgres://localhost/mydb"
REDIS_URL: "redis://localhost:6379"
frontend:
cmd: "npm run dev -- --port 3000"
depends_on: [api]Service Options
| Option | Type | Default | Description |
|-------------|----------|--------------|------------------------------------------|
| cmd | string | required | Command to start the service |
| depends_on| string[] | [] | Services that must be healthy first |
| health | object | none | Health check configuration |
| env | object | {} | Environment variables |
| cwd | string | process.cwd() | Working directory |
| restart | string | on-failure | Restart policy: always, on-failure, never |
Health Check Options
| Option | Type | Default | Description |
|-----------|--------|---------|-------------------------------------|
| tcp | string | - | TCP host:port to check |
| http | string | - | HTTP URL (expects 2xx) |
| cmd | string | - | Command (expects exit code 0) |
| interval| string | 1s | Time between checks |
| timeout | string | 30s | Overall timeout |
| retries | number | 3 | Number of retries before giving up |
Only one of tcp, http, or cmd can be specified per health check.
Example Configs
Node.js + PostgreSQL
services:
postgres:
cmd: "docker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=dev postgres:16"
health:
tcp: "localhost:5432"
timeout: 15s
retries: 5
app:
cmd: "npm run dev"
depends_on: [postgres]
health:
http: "http://localhost:3000/health"
env:
DATABASE_URL: "postgres://postgres:dev@localhost:5432/mydb"Python + Redis
services:
redis:
cmd: "redis-server --port 6380"
health:
tcp: "localhost:6380"
celery:
cmd: "celery -A tasks worker --loglevel=info"
depends_on: [redis]
env:
CELERY_BROKER_URL: "redis://localhost:6380/0"
restart: on-failure
api:
cmd: "uvicorn main:app --reload --port 8000"
depends_on: [redis]
health:
http: "http://localhost:8000/health"
env:
REDIS_URL: "redis://localhost:6380/0"Full-stack (React + Express + PostgreSQL + Redis)
services:
postgres:
cmd: "docker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=dev postgres:16"
health:
tcp: "localhost:5432"
timeout: 15s
redis:
cmd: "docker run --rm -p 6379:6379 redis:7"
health:
tcp: "localhost:6379"
api:
cmd: "npm run dev"
cwd: "./packages/api"
depends_on: [postgres, redis]
health:
http: "http://localhost:4000/health"
env:
DATABASE_URL: "postgres://postgres:dev@localhost:5432/app"
REDIS_URL: "redis://localhost:6379"
web:
cmd: "npm run dev"
cwd: "./packages/web"
depends_on: [api]
env:
VITE_API_URL: "http://localhost:4000"Troubleshooting
Config file not found
Config error: Config file not found: /path/to/depstart.yamlRun depstart init to generate a sample config, or specify a path with depstart up -c path/to/config.yaml.
Service fails to start
- Check that the command works standalone: run
cmddirectly in your terminal - Verify that any ports are not already in use
- Check the
cwdpath is correct (relative to where you run depstart)
Health check always fails
- TCP: Ensure the service is binding to the correct host/port. Use
127.0.0.1instead oflocalhostif DNS resolution is slow. - HTTP: Verify the endpoint returns a 2xx status code. The URL must be fully qualified (e.g.,
http://localhost:3000/health). - Command: Run the health check command manually to ensure it exits with code 0.
- Increase
timeoutandretriesfor services that take longer to boot.
Cyclic dependency error
Dependency error: Cyclic dependency detected: a -> b -> aReview depends_on fields. Two services cannot depend on each other. Restructure your services so dependencies form a DAG (directed acyclic graph).
Process keeps restarting
The default restart policy is on-failure. Set restart: never to disable auto-restart, or restart: always if you want the service to always restart (even on clean exit).
FAQ
Q: Can I use depstart with Docker Compose?
A: Yes. You can use depstart to orchestrate services that run Docker containers via their cmd field. See the Integration Patterns docs for details.
Q: What happens when I press Ctrl+C?
A: depstart sends SIGTERM to all managed processes, waits up to 5 seconds for graceful shutdown, then sends SIGKILL. On Windows, taskkill /F is used.
Q: Can services run in parallel? A: Yes. Services without dependencies (or whose dependencies are all healthy) start in parallel. depstart uses topological sort to determine layers.
Q: Does depstart support .env files?
A: Not yet. For now, use the env field in depstart.yaml or set environment variables in your shell before running depstart.
Q: What restart policies are available?
A: on-failure (default) restarts only on non-zero exit, always restarts on any exit, never never restarts. Restarts use exponential backoff (1s, 2s, 4s, ... up to 30s).
Integration Patterns
Alongside Docker Compose
depstart and docker-compose serve different purposes and work well together. Use docker-compose for infrastructure (databases, message queues) and depstart for your application services:
# docker-compose.yaml - infrastructure only
services:
postgres:
image: postgres:16
ports: ["5432:5432"]
environment:
POSTGRES_PASSWORD: dev
redis:
image: redis:7
ports: ["6379:6379"]# depstart.yaml - application services that depend on docker-compose infra
services:
infra:
cmd: "docker compose up"
health:
tcp: "localhost:5432"
timeout: 30s
retries: 10
api:
cmd: "npm run dev"
depends_on: [infra]
health:
http: "http://localhost:3000/health"
env:
DATABASE_URL: "postgres://postgres:dev@localhost:5432/app"
worker:
cmd: "npm run worker"
depends_on: [infra]
env:
REDIS_URL: "redis://localhost:6379"With Makefile
.PHONY: dev stop
dev:
depstart up
stop:
@echo "Press Ctrl+C on the depstart process to stop services"
validate:
depstart validate
ci:
npm run ciProgrammatic Usage
depstart exports its core modules for use in scripts and custom tools:
import { parseConfig, resolveOrder, ProcessManager, OutputMultiplexer } from 'depstart';
const services = parseConfig(yamlString);
const layers = resolveOrder(services);
const output = new OutputMultiplexer();
const manager = new ProcessManager(output);
for (const layer of layers) {
await Promise.all(layer.map(svc => manager.startService(svc)));
}
// Later...
await manager.stopAll();With npm scripts
{
"scripts": {
"dev": "depstart up",
"dev:validate": "depstart validate",
"dev:status": "depstart status"
}
}How It Works
depstart orchestrates your local development services in four phases:
1. Config Parsing & Validation
When you run depstart up, it reads depstart.yaml and validates every field: service commands, dependency references, health check formats, restart policies, and environment variables. Invalid configs fail fast with clear error messages.
2. DAG Resolution (Topological Sort)
Services and their depends_on relationships form a directed acyclic graph (DAG). depstart uses Kahn's algorithm to sort services into layers:
Layer 0: [postgres, redis] -- no dependencies, start first
Layer 1: [api] -- depends on postgres + redis
Layer 2: [web] -- depends on apiIf a cycle is detected (e.g., A depends on B, B depends on A), depstart reports the exact cycle path and exits.
3. Parallel Start with Health Polling
Each layer starts all its services in parallel. For each service:
- The command is spawned as a child process
- stdout/stderr are captured and multiplexed with colored prefixes
- If a health check is configured, depstart polls at the configured
interval - The service is marked healthy when the check passes, or unhealthy after
retriesfailures ortimeoutexpires
depstart waits for all services in a layer to become healthy before starting the next layer.
4. Steady State & Shutdown
Once all layers are started, depstart keeps running and streaming logs. On Ctrl+C (SIGINT/SIGTERM):
- All child processes receive SIGTERM (or
taskkill /Fon Windows) - depstart waits up to 5 seconds for graceful shutdown
- Remaining processes are killed with SIGKILL
- depstart exits
Services with restart: on-failure automatically restart with exponential backoff (1s, 2s, 4s, ... up to 30s) if they crash.
Architecture
depstart.yaml
|
v
[Config Parser] --> validates YAML, fields, references
|
v
[DAG Resolver] --> topological sort into parallel layers
|
v
[Process Manager] --> spawns processes, manages lifecycle
| |
v v
[Health Checker] [Output Multiplexer]
polls tcp/http/cmd colored log prefixesLicense
MIT
