| Crates.io | service-federation |
| lib.rs | service-federation |
| version | 1.1.0 |
| created_at | 2026-01-23 22:50:40.388776+00 |
| updated_at | 2026-01-25 22:20:46.583402+00 |
| description | Service federation orchestrator for managing complex service dependencies |
| homepage | https://github.com/service-federation/service-federation |
| repository | https://github.com/service-federation/service-federation |
| max_upload_size | |
| id | 2065602 |
| size | 1,650,024 |
Local development orchestration with session-based port stability.
fed runs Docker containers, native processes, and Gradle tasks together with dependency resolution and port allocation. Sessions remember your ports across restarts.
cargo install service-federation
service-federation.yamlparameters:
API_PORT:
type: port
default: 8080 # Prefers 8080, falls back if unavailable
services:
database:
image: postgres:15
ports: ["5432:5432"]
environment:
POSTGRES_PASSWORD: password
backend:
process: npm start
cwd: ./backend
depends_on: [database]
environment:
PORT: '{{API_PORT}}'
DATABASE_URL: 'postgres://localhost:5432/db'
healthcheck:
httpGet: 'http://localhost:{{API_PORT}}/health'
frontend:
process: npm run dev
cwd: ./frontend
depends_on: [backend]
environment:
BACKEND_URL: 'http://localhost:{{API_PORT}}'
entrypoint: frontend
# Start all services (follows dependencies)
fed start
# Check status
fed status
# View logs
fed logs backend --tail 50
# Stop all
fed stop
Sessions remember port allocations across restarts.
# Start a session (creates .fed/session file)
fed session start --id my-project
# Start postgres
fed start postgres
# → postgres gets port 5432
# Stop postgres
fed stop postgres
# Start postgres again
fed start postgres
# → postgres gets 5432 again
# Clean up when done
fed session end
Sessions are auto-detected from .fed/session in your project directory.
Run any command:
services:
api:
process: npm start
cwd: ./api
services:
redis:
image: redis:7-alpine
ports: ["6379:6379"]
Reuse existing docker-compose.yml:
services:
postgres:
composeFile: ./docker-compose.yml
composeService: postgres
Groups tasks by directory:
# Same cwd → batched into one gradle command
services:
auth-service:
gradleTask: ':auth:bootRun'
cwd: ./backend
environment:
SERVER_PORT: '{{AUTH_PORT}}'
user-service:
gradleTask: ':user:bootRun'
cwd: ./backend # Same working dir → runs with auth-service
environment:
SERVER_PORT: '{{USER_PORT}}'
payment-service:
gradleTask: ':payment:bootRun'
cwd: ./backend # Same working dir → all three run as: gradle :auth:bootRun :user:bootRun :payment:bootRun
environment:
SERVER_PORT: '{{PAYMENT_PORT}}'
Gradle initializes once instead of per-task.
Define base configurations to extend:
templates:
java-service:
image: openjdk:17-slim
environment:
JAVA_OPTS: '-Xmx512m'
healthcheck:
httpGet: 'http://localhost:{{PORT}}/actuator/health'
restart: always
services:
auth-service:
extends: java-service # Inherits all template fields
ports: ["8080:8080"]
environment:
PORT: '8080' # Add or override specific values
user-service:
extends: java-service # Reuse same template
ports: ["8081:8081"]
environment:
PORT: '8081'
See examples/templates-example.yaml for a complete example.
Manage service setup, build, and cleanup with lifecycle hooks:
services:
backend:
process: npm start
cwd: ./backend
install: npm ci # Run before first start
build: npm run build # Run with `fed build`
clean: rm -rf node_modules dist # Run with `fed clean`
environment:
PORT: '{{API_PORT}}'
database:
image: postgres:15
volumes:
- "fed-db-data:/var/lib/postgresql/data" # Named volume
Install hooks:
fed install (forces re-run)fed clean (install will run again on next start)Build hooks:
fed build (always runs, not tracked)cwd with its environment variablesClean hooks:
fed- prefix./data:/data) are NOT removed (only named volumes)fed-* prefixed Docker volumes are removed to prevent accidental data lossparameters:
API_PORT:
type: port
default: 8080 # Tries 8080, falls back to random available
DB_PORT:
type: port # Always random available port
Use in services:
services:
api:
environment:
PORT: '{{API_PORT}}'
DATABASE_URL: 'postgres://localhost:{{DB_PORT}}/db'
services:
database:
process: postgres -D data
backend:
depends_on: [database] # Waits for database to be healthy
frontend:
depends_on: [backend] # Waits for backend
Scripts are automation commands that can depend on services or other scripts:
scripts:
db:migrate:
depends_on: [database]
script: npx prisma db push
test:integration:
depends_on: [db:migrate, api] # Can depend on scripts AND services
environment:
DATABASE_URL: postgres://localhost/test
script: npm run test:e2e
Run scripts:
fed run db:migrate # Run a script
fed db:migrate # Shorthand (if no command collision)
fed test:integration -- -t "auth" # Pass arguments after --
Receiving arguments in scripts: Use "$@" to receive arguments passed after --:
scripts:
test:integration:
script: npm run test:integration -- "$@" # Passes args to npm
Then fed test:integration -- --run file.ts becomes npm run test:integration -- --run file.ts.
Important: Services started as script dependencies continue running after the script completes. To stop them, use fed stop manually.
Run tests with fresh ports and volumes while your dev stack is running:
scripts:
test:integration:
isolated: true # Fresh ports, scoped volumes, isolated services
depends_on: [database, api]
script: npm run test:e2e
How it works:
myvolume → fed-{session}-myvolume)Use case:
# Terminal 1: Dev stack on default ports
fed start
# Terminal 2: Tests on isolated random ports (doesn't affect dev stack)
fed test:integration
Run tests without stopping your dev stack.
Use .env files to provide parameter values without modifying your config file:
parameters:
API_KEY:
default: "" # Declare the parameter
DATABASE_URL:
default: "postgres://localhost:5432/dev"
# .env files set parameter values (variables MUST be declared above)
env_file:
- .env # API_KEY=secret123
- .env.local # Later files override earlier
services:
api:
environment:
# Reference parameters (values come from .env via parameters)
API_KEY: '{{API_KEY}}'
DATABASE_URL: '{{DATABASE_URL}}'
PORT: '{{API_PORT}}'
How it works:
.env files set parameter values, not service environment directly.env files must be declared as parameters in your configservice-federation.yamlPriority (highest to lowest):
value: fieldenv_filedefault: valueFeatures:
.env format (KEY=VALUE, comments with #, quoting)See examples/env-file/ for a complete example.
services:
api:
healthcheck:
httpGet: 'http://localhost:{{API_PORT}}/health'
timeout: 5s # Optional, default 30s
database:
image: postgres:15
healthcheck:
command: pg_isready -U postgres # Runs INSIDE the container
timeout: 10s
Health check types:
httpGet: HTTP request from host to check if service is respondingcommand: Shell command to verify service healthImportant behavior:
command health checks run inside the container via docker execcommand health checks run on the hosthttpGet always runs from the hostLimit memory, CPU, and processes:
services:
database:
image: postgres:15
resources:
memory: 512m # Max memory usage
cpus: "2.0" # Max CPU cores
pids: 100 # Max number of processes/threads
nofile: 1024 # Max open file descriptors
backend:
process: npm start
cwd: ./backend
resources:
memory: 1g # Supports: b, k/kb, m/mb, g/gb, t/tb
cpus: "1.0" # Decimal format: "0.5", "2.0", etc.
pids: 50 # Prevent fork bombs
# Docker-specific resource options
worker:
image: worker:latest
resources:
memory: 256m
memory_reservation: 128m # Soft limit (Docker only)
memory_swap: 512m # Swap limit (Docker only)
cpu_shares: 512 # Relative CPU weight (Docker only)
Docker Services:
memory: Hard memory limit via --memorymemory_reservation: Soft limit via --memory-reservationmemory_swap: Swap limit via --memory-swapcpus: CPU limit via --cpuscpu_shares: Relative weight via --cpu-sharespids: Process limit via --pids-limitnofile: File descriptor limit via --ulimit nofileProcess Services (Unix/Linux):
memory: Address space limit via RLIMIT_ASpids: Process limit via RLIMIT_NPROC (Linux)nofile: File descriptor limit via RLIMIT_NOFILEMemory Format:
4096, 8192b512k, 512kb256m, 256mb (recommended)2g, 2gb1t, 1tb1.5g (1.5 gigabytes)Without limits:
With limits:
Conditionally include services based on active profiles:
services:
api:
process: npm start
# No profiles = always included when no profiles are active
worker:
process: npm run worker
profiles: [worker] # Only started when 'worker' profile is active
debug-tools:
image: debug:latest
profiles: [debug, development] # Started when 'debug' OR 'development' profile is active
Usage:
fed start # Starts only services without profiles (api)
fed start -p worker # Starts api + worker
fed start -p worker -p debug # Starts api + worker + debug-tools
fed start --profile development # Starts api + debug-tools
Profile behavior:
profiles are always started when no profiles are activeprofiles are only started when at least one matching profile is active-p/--profile flags can be combinedLimit restarts to prevent crash loops:
services:
flaky-service:
process: ./might-crash.sh
restart: always
circuit_breaker:
threshold: 5 # Max restarts before tripping
cooldown: 60s # Wait time before allowing restarts again
When a service restarts more than threshold times, the circuit breaker "trips" and prevents further restarts until cooldown expires. This prevents infinite crash loops from consuming resources.
Control how long to wait before force-killing a service:
services:
api:
process: npm start
grace_period: 30s # Wait 30s for graceful shutdown before SIGKILL (default: 10s)
Tag services for grouping:
services:
api:
process: npm start
tags: [backend, critical]
worker:
process: npm run worker
tags: [backend, async]
frontend:
process: npm run dev
tags: [frontend]
Usage:
fed start @backend # Start all services tagged 'backend' (api, worker)
fed stop @critical # Stop all services tagged 'critical' (api)
# Service management
fed start # Start all services in background (default)
fed start -w # Start with watch mode (foreground, auto-restart on file changes)
fed start postgres redis # Start specific services
fed start --replace # Kill processes occupying required ports, then start
fed start --dry-run # Preview what would happen without starting
fed start --output file # Output mode: file (background), captured (memory), passthrough (stdio)
fed stop # Stop all services
fed stop postgres # Stop specific service
fed restart backend # Restart specific service
fed status # Show service status
fed status --json # Show service status as JSON (for scripting)
fed logs backend --tail 50 # View service logs
fed logs backend --follow # Stream logs (Ctrl+C to stop)
fed top # Show resource usage (CPU, memory, PID)
# Build lifecycle
fed install # Run install commands for all services
fed install backend # Run install for specific service
fed build # Run build commands for all services
fed build backend # Build specific service
fed clean # Run clean commands and remove Docker volumes
fed clean backend # Clean specific service
# Configuration management
fed init # Create starter service-federation.yaml
fed validate # Validate config without starting services
fed doctor # Check system requirements (Docker, Gradle, Java, etc.)
fed port backend # Quickly show port for a service
# Session management
fed session start --id dev # Start named session
fed session list # List all sessions
fed session end # End current session
fed session cleanup # Clean up orphaned sessions
# Developer experience
fed completions bash # Generate shell completions (bash/zsh/fish)
fed tui # Interactive TUI (beta)
fed run test # Run script from config
fed test # Shorthand for scripts (if no command collision)
fed test -- -t "specific" # Pass arguments to script
fed --config dev.yaml start # Use specific config file
See the examples/ directory for complete configurations:
simple.yaml - Basic multi-service setupscripts-example.yaml - Scripts with dependencies and argument passthroughenv-file/ - Environment file (.env) supporttemplates-example.yaml - Reusable service templatesresource-limits-example.yaml - Resource limitsdocker-compose-example/ - Integrating with existing docker-composegradle-grouping.yaml - Gradle task batchingcomplex-dependencies/ - Multi-level dependency graphprofiles-example.yaml - Environment-specific configs# Start a session in your project directory
cd ~/my-project
fed session start --id my-project
# This creates .fed/session file - now all fed commands
# in this directory will use the session automatically
# Add .fed/ to .gitignore
echo ".fed/" >> .gitignore
Alternative: If you need to use the session in multiple directories, export the env var:
export FED_SESSION=my-project
services:
api:
healthcheck:
httpGet: 'http://localhost:{{PORT}}/health'
frontend:
depends_on: [api] # Waits for api to be healthy
services:
# Use existing docker-compose for databases
postgres:
composeFile: docker-compose.yml
composeService: postgres
# Run your app natively
backend:
process: cargo run --bin api
depends_on: [postgres]
Services not starting?
fed logs <service-name> --tail 100
Port conflicts?
# Use port parameters instead of hardcoded ports
parameters:
API_PORT:
type: port
default: 8080 # Auto-falls back if in use
Stuck sessions?
fed session cleanup
Features:
isolated: true for fresh ports and scoped volumes--follow flag and search/filterfed start -w (auto-restart on file changes)Roadmap:
Issues and PRs welcome! This is an early-stage project.
MIT