#dev-environment #xtask #docker

devforge

Dev environment orchestrator — docker, health checks, mprocs, custom commands via TOML config

3 releases (breaking)

new 0.3.0 Feb 26, 2026
0.2.0 Feb 26, 2026
0.1.0 Feb 25, 2026

#1142 in Development tools

MIT license

26KB
578 lines

devforge

Config-driven dev environment orchestrator for Rust workspaces. Define your Docker services, health checks, hooks, and process manager in a single TOML file.

Quick Start

1. Add the xtask crate to your workspace

# xtask/Cargo.toml
[package]
name = "xtask"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
devforge = "0.3"
// xtask/src/main.rs
fn main() {
    devforge::run();
}

2. Add the cargo alias

# .cargo/config.toml
[alias]
xtask = "run --package xtask --"

3. Create devforge.toml at your workspace root

env_files = [".env"]
required_tools = ["docker", "cargo", "mprocs"]

[docker]
compose_file = "docker-compose.yml"

[[docker.health_checks]]
name = "postgres"
cmd = ["docker", "compose", "exec", "-T", "postgres", "pg_isready", "-U", "myuser"]
timeout = 30

[[docker.health_checks]]
name = "redis"
tcp = "localhost:6379"
timeout = 15

[dev]
mprocs_config = "mprocs.yaml"

[[dev.hooks]]
cmd = "npm install"
cwd = "web"

[dev.hooks.condition]
missing = "web/node_modules"

4. Run it

cargo xtask dev     # Start everything
cargo xtask infra   # Docker only (Ctrl+C to stop)

Commands

Command Description
dev Preflight checks, docker compose up, health checks, pre-launch hooks, process runner. Docker tears down automatically on exit.
infra Docker compose up + health checks. Blocks until Ctrl+C, then tears down.
Custom Any [[commands]] entry defined in your TOML.

Configuration Reference

Top-level

# Files that must exist before starting (checked in order)
env_files = [".env", "web/.env.local"]

# Commands that must be in PATH
required_tools = ["docker", "cargo", "node", "npm", "mprocs"]

Files listed in env_files are loaded into the process environment if they have no extension or a .env extension (e.g. .env, .env.local).

Entries can also use the object form with a template field. If the target file doesn't exist, devforge copies the template before running preflight checks:

[[env_files]]
path = "safe-route/.env"
template = "safe-route/.env.example"

[docker]

[docker]
compose_file = "docker-compose.yml"  # default

[[docker.health_checks]]

Three types of health checks (exactly one of cmd, url, or tcp must be set):

Command-based -- runs a command, success = exit code 0:

[[docker.health_checks]]
name = "postgres"
cmd = ["docker", "compose", "exec", "-T", "postgres", "pg_isready", "-U", "myuser"]
timeout = 30  # seconds, default 30

URL-based -- HTTP GET, success = 2xx response (must use http:// or https://):

[[docker.health_checks]]
name = "minio"
url = "http://localhost:9000/minio/health/live"
timeout = 30

TCP-based -- connects to a TCP socket, success = connection established:

[[docker.health_checks]]
name = "redis"
tcp = "localhost:6379"
timeout = 30

Health checks poll every 1 second until success or timeout. On failure, the error includes the service name, check type, and specific reason:

[devforge] ✗ health check failed: postgres (cmd: docker compose exec ...) — exit code 1: could not connect

[dev]

[dev]
mprocs_config = "mprocs.yaml"  # default

[dev.runner]

Controls which process manager the dev command uses. Defaults to mprocs if omitted.

# Explicit mprocs (same as omitting the block entirely)
[dev.runner]
type = "mprocs"

# Run an arbitrary shell command instead of mprocs
[dev.runner]
type = "shell"
cmd = "npm run dev"

# No process manager — just run hooks, then block until Ctrl+C
[dev.runner]
type = "none"

[[dev.hooks]]

Hooks run before the process runner launches. They execute via sh -c.

[[dev.hooks]]
cmd = "npm install"
cwd = "web"              # optional, relative to workspace root

[dev.hooks.condition]
missing = "web/node_modules"  # only run if this path doesn't exist

[[commands]]

Define custom commands accessible via cargo xtask <name>:

[[commands]]
name = "migrate"
cmd = ["cargo", "run", "--release", "--package", "my-api", "--", "migrate"]
description = "Run database migrations"
docker = true  # start docker infra first (default: false)

How It Works

cargo xtask dev
    |
    v
Preflight: check env files, load .env, verify tools
    |
    v
docker compose up -d
    |
    v
Health checks (poll until ready or timeout)
    |
    v
Pre-launch hooks (conditional)
    |
    v
Process runner (mprocs TUI / shell cmd / Ctrl+C wait)
    |
    v  (on exit)
docker compose down

License

MIT

Dependencies

~11–22MB
~363K SLoC