devhub.toml Reference
The devhub.toml file is the project manifest that tells DevHub how to manage your project’s services.
Basic Structure
[project]
name = "my-project"
description = "My awesome project"
tags = ["rust", "web", "api"]
env_files = [".env", ".env.local"]
[[services]]
name = "api"
type = "rust-binary"
command = "cargo run --release"
port = 8080
health_check = "/health"
subdomain = "api"
env_file = "api/.env"
[[services]]
name = "frontend"
type = "node"
command = "npm run dev"
cwd = "frontend"
port = 3000
main = true
depends_on = ["api"]
[environment]
RUST_LOG = "info"
DATABASE_URL = "postgres://localhost/mydb"
[project] Section
Project-level metadata.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique project identifier |
description | string | No | Human-readable description |
tags | array | No | Tags for organization/filtering |
env_files | array | No | Environment files to load for all services |
Example
[project]
name = "my-fullstack-app"
description = "Full-stack application with API and frontend"
tags = ["typescript", "react", "node", "postgres"]
env_files = [".env", ".env.local"]
[[services]] Section
Define one or more services. Each service represents a runnable process.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Service identifier |
type | string | Yes | - | Service type (see below) |
command | string | Yes | - | Command to run |
port | integer | Yes | - | Port the service listens on |
cwd | string | No | project root | Working directory (relative) |
health_check | string | No | - | HTTP endpoint for health checks |
subdomain | string | No | - | Subdomain for reverse proxy |
main | boolean | No | false | Primary service (no subdomain) |
depends_on | array | No | [] | Services to start first |
env | table | No | {} | Service-specific env vars |
env_file | string | No | - | Service-specific env file path |
Service Types
| Type | Detection | Default Behavior |
|---|---|---|
rust-binary | Cargo.toml | Native process |
node | package.json | PM2 if available, otherwise native |
python | pyproject.toml | Native process |
go | go.mod | Native process |
docker-compose | docker-compose.yml | Docker Compose commands |
shell | Any | Native process |
Example Service
[[services]]
name = "api"
type = "rust-binary"
command = "cargo run --bin api --release"
port = 8080
health_check = "/api/health"
subdomain = "api"
env = { "RUST_LOG" = "debug", "DATABASE_URL" = "postgres://localhost/mydb" }
env_file = "api/.env"
[environment] Section
Global environment variables applied to all services.
[environment]
NODE_ENV = "development"
RUST_LOG = "info"
DATABASE_URL = "postgres://localhost/mydb"
API_KEY = "dev-key-123"
Environment Loading Priority
Environment variables are loaded in this order (later sources override earlier):
- System environment variables
- Project root
.envfile - Project root
.env.localfile - Files listed in
env_files [environment]section in manifest- Service-specific
env_file - Service
cwd/.envfile - Service
cwd/.env.localfile - Service
env = {}section - DevHub service URL injection
- Service reference expansion
Variable Interpolation
Variables can reference other variables using $VAR or ${VAR} syntax:
[environment]
DB_HOST = "localhost"
DB_PORT = "5432"
DB_NAME = "myapp"
DATABASE_URL = "postgres://${DB_HOST}:${DB_PORT}/${DB_NAME}"
Service URL Environment Variables
DevHub automatically injects environment variables for service-to-service communication. This eliminates the need to hardcode localhost:PORT in your .env files.
Auto-Injected Variables
For every service in your project, DevHub injects:
| Variable | Example | Description |
|---|---|---|
DEVHUB_<SERVICE>_URL | http://api.myapp.localhost | External URL via Caddy reverse proxy |
DEVHUB_<SERVICE>_INTERNAL_URL | http://myapp-api:8080 | Internal URL for container-to-container |
DEVHUB_<SERVICE>_PORT | 8080 | Service port |
Service names are converted to uppercase with hyphens replaced by underscores (e.g., my-api → MY_API).
View all injected variables with:
devhub env my-project --service frontend | grep DEVHUB_
Service Reference Syntax
You can reference other services in your devhub.toml using the ${service.property} syntax:
[[services]]
name = "frontend"
type = "node"
command = "npm run dev"
port = 3000
main = true
[services.env]
# These get auto-expanded by DevHub:
NEXT_PUBLIC_API_URL = "${backend.url}" # → http://backend.myapp.localhost
INTERNAL_API_URL = "${backend.internal}" # → http://myapp-backend:8080
API_PORT = "${backend.port}" # → 8080
Available properties:
| Property | Description |
|---|---|
${service.url} | External URL via Caddy (for browser/client-side requests) |
${service.internal} | Internal URL for container-to-container communication |
${service.port} | Service port number |
Use Case: Frontend + Backend
A common pattern is a Next.js frontend calling a Python/Node/Rust backend:
[project]
name = "my-fullstack-app"
[[services]]
name = "backend"
type = "python"
command = "uvicorn main:app --host 0.0.0.0 --port 8081"
port = 8081
subdomain = "api"
[[services]]
name = "frontend"
type = "node"
command = "npm run dev"
port = 3000
main = true
depends_on = ["backend"]
[services.env]
# Client-side API calls go through Caddy
NEXT_PUBLIC_API_URL = "${backend.url}"
The frontend automatically receives NEXT_PUBLIC_API_URL=http://api.my-fullstack-app.localhost.
Benefits:
- No hardcoded
localhost:PORTin.envfiles - Works consistently across native and container modes
- URLs update automatically based on project name
- Supports both browser requests (external URLs) and server-side requests (internal URLs)
Monorepo Configuration
For projects with multiple services in subdirectories:
[project]
name = "my-monorepo"
description = "Fullstack monorepo"
env_files = [".env"]
[[services]]
name = "api"
type = "python"
command = "uvicorn app:app --host 0.0.0.0 --port 8000"
cwd = "backend"
port = 8000
subdomain = "api"
[[services]]
name = "web"
type = "node"
command = "npm run dev"
cwd = "frontend"
port = 3000
main = true
depends_on = ["api"]
[[services]]
name = "admin"
type = "node"
command = "npm run dev"
cwd = "admin-panel"
port = 3001
subdomain = "admin"
depends_on = ["api"]
Service Dependencies
Use depends_on to control startup order:
[[services]]
name = "database"
type = "docker-compose"
command = "docker compose up -d postgres"
port = 5432
[[services]]
name = "api"
type = "rust-binary"
command = "cargo run"
port = 8080
depends_on = ["database"]
[[services]]
name = "frontend"
type = "node"
command = "npm run dev"
port = 3000
depends_on = ["api"]
Services start in dependency order: database → api → frontend
Complete Example
[project]
name = "ecommerce-platform"
description = "Full-stack e-commerce application"
tags = ["typescript", "rust", "postgres", "redis"]
env_files = [".env", ".env.local"]
[[services]]
name = "postgres"
type = "docker-compose"
command = "docker compose up -d postgres"
port = 5432
[[services]]
name = "redis"
type = "docker-compose"
command = "docker compose up -d redis"
port = 6379
[[services]]
name = "api"
type = "rust-binary"
command = "cargo run --bin api --release"
cwd = "services/api"
port = 8080
health_check = "/health"
subdomain = "api"
depends_on = ["postgres", "redis"]
env_file = "services/api/.env"
[[services]]
name = "worker"
type = "rust-binary"
command = "cargo run --bin worker --release"
cwd = "services/worker"
port = 8081
depends_on = ["postgres", "redis"]
[[services]]
name = "storefront"
type = "node"
command = "npm run dev"
cwd = "apps/storefront"
port = 3000
main = true
depends_on = ["api"]
[[services]]
name = "admin"
type = "node"
command = "npm run dev"
cwd = "apps/admin"
port = 3001
subdomain = "admin"
depends_on = ["api"]
[environment]
NODE_ENV = "development"
RUST_LOG = "info"
DATABASE_URL = "postgres://localhost:5432/ecommerce"
REDIS_URL = "redis://localhost:6379"