# Ecosystem Config Reference > Define multiple processes in a single file and start them all with one command. --- ## Overview An ecosystem config file lets you define all your application processes in one place — their scripts, arguments, environment variables, restart policies, and more. Both **TOML** and **JSON** formats are supported. ```powershell # Start all apps defined in the config alter start alter.config.toml alter start alter.config.json ``` --- ## TOML Format ```toml # alter.config.toml [[apps]] name = "api" script = "python" args = ["-m", "uvicorn", "main:app", "--port", "8000"] cwd = "C:\\projects\\api" autorestart = true max_restarts = 10 restart_delay_ms = 2000 namespace = "web" max_log_size_mb = 25 [apps.env] PORT = "8000" DATABASE_URL = "postgres://localhost/mydb" DEBUG = "false" [[apps]] name = "worker" script = "node" args = ["dist/worker.js"] cwd = "C:\\projects\\worker" autorestart = true max_restarts = 5 restart_delay_ms = 5000 namespace = "workers" watch = true watch_paths = ["dist/"] watch_ignore = ["node_modules", "*.log", "*.map"] [apps.env] NODE_ENV = "production" QUEUE = "default" [[apps]] name = "scheduler" script = "go" args = ["run", "cmd/scheduler/main.go"] cwd = "C:\\projects\\scheduler" namespace = "workers" autorestart = true [apps.env] TZ = "UTC" ``` --- ## JSON Format ```json { "apps": [ { "name": "api", "script": "python", "args": ["-m", "uvicorn", "main:app", "--port", "8000"], "cwd": "C:\\projects\\api", "autorestart": true, "max_restarts": 10, "namespace": "web", "env": { "PORT": "8000", "DATABASE_URL": "postgres://localhost/mydb" } }, { "name": "worker", "script": "node", "args": ["dist/worker.js"], "watch": true, "watch_paths": ["dist/"], "namespace": "workers", "env": { "NODE_ENV": "production" } } ] } ``` --- ## Field Reference | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `name` | string | **yes** | — | Display name. Used as process identifier in CLI commands | | `script` | string | **yes** | — | Executable to run (`python`, `node`, `go`, `dotnet`, etc.) | | `args` | string[] | no | `[]` | Arguments passed to the script | | `cwd` | string | no | current dir | Working directory. Use absolute paths. On Windows, use double backslashes or forward slashes | | `env` | object | no | `{}` | Environment variables as key-value string pairs | | `autorestart` | bool | no | `true` | Automatically restart if the process exits with a non-zero code | | `max_restarts` | integer | no | `10` | Maximum number of restart attempts before marking as `errored` | | `restart_delay_ms` | integer | no | `1000` | Base delay in milliseconds before the first restart. Doubles on each attempt (capped at 60s) | | `namespace` | string | no | `"default"` | Logical group for organizing processes in the dashboard | | `watch` | bool | no | `false` | Enable watch mode — restart the process when files change | | `watch_paths` | string[] | no | `[]` | Directories or files to watch. Relative paths resolved from `cwd` | | `watch_ignore` | string[] | no | `[]` | Glob patterns to exclude from watching | | `max_log_size_mb` | integer | no | `10` | Rotate log file when it exceeds this size (in megabytes) | | `instances` | integer | no | `1` | Reserved — parsed but not yet active | | `log_file` | string | no | auto | Custom path for stdout log. Defaults to `%APPDATA%\alter-pm2\logs\\out.log` | | `error_file` | string | no | auto | Custom path for stderr log. Defaults to `%APPDATA%\alter-pm2\logs\\err.log` | | `notify` | object | no | null | Per-process notification config override (see [Notifications](#notifications)) | | `env_file` | string | no | null | Path to a `.env` file — loaded and merged with `env` (explicit `env` wins on conflict) | | `health_check_url` | string | no | null | HTTP (`http://...`) or TCP (`host:port`) probe URL for health checks | | `health_check_interval_secs` | integer | no | `30` | Seconds between health probes | | `health_check_timeout_secs` | integer | no | `5` | Seconds before a probe times out | | `health_check_retries` | integer | no | `3` | Consecutive failures before marking as unhealthy and firing a notification | | `pre_start` | string | no | null | Shell command to run before the process starts (blocks start on failure) | | `post_start` | string | no | null | Shell command to run after the process starts (non-blocking, failures are logged) | | `pre_stop` | string | no | null | Shell command to run before the process is killed (failures are logged) | --- ## Field Details ### `script` The executable to run. alter handles the following automatically: - **Python:** `python`, `python3`, `py` - **Node.js:** `node` - **Go:** `go` (e.g. `args = ["run", "main.go"]`) - **Rust:** `cargo` (e.g. `args = ["run", "--release"]`) - **.NET:** `dotnet` - **PHP:** `php` - **Ruby:** `ruby` - **Any `.exe`** on Windows — spawned directly - **Batch scripts** (`.cmd`) — automatically wrapped in `cmd /C` > **Windows note:** Tools like `npm`, `yarn`, `npx`, `tsc`, `nodemon` are `.cmd` batch files. alter wraps them in `cmd /C` automatically, so you can use them directly as the `script` value. ```toml # These all work on Windows: script = "npm" args = ["run", "start"] script = "npx" args = ["nodemon", "index.js"] script = "dotnet" args = ["MyApp.dll"] ``` ### `cwd` Working directory for the process. If not specified, the daemon's working directory is used. ```toml # Windows (double backslash) cwd = "C:\\Users\\me\\projects\\api" # Windows (forward slash also works) cwd = "C:/Users/me/projects/api" ``` ### `env` Environment variables are merged with the system environment. The process inherits all existing environment variables, plus any you define here. ```toml [apps.env] NODE_ENV = "production" PORT = "3000" DATABASE_URL = "postgres://localhost/mydb" API_KEY = "secret" ``` ### `autorestart` and `max_restarts` ```toml autorestart = true # restart on crash max_restarts = 10 # give up after 10 attempts ``` A **clean exit** (code 0) is **not** treated as a crash — the process stays in `stopped` state and is not restarted. ### `restart_delay_ms` Exponential backoff delay: ``` attempt 0: 1000ms (base) attempt 1: 2000ms attempt 2: 4000ms attempt 3: 8000ms ... attempt 8+: 60000ms (capped) ``` Setting a higher base (e.g. `5000`) extends all delays proportionally. ### `watch` and `watch_paths` ```toml watch = true watch_paths = ["src/", "config/"] watch_ignore = ["node_modules", "__pycache__", "*.log", "*.pyc"] ``` Watch mode is ideal for development. The process restarts automatically after a 500ms debounce whenever watched files change. ### `namespace` Namespaces appear as collapsible groups in the web dashboard. They have no effect on process behavior. ```toml namespace = "web" # dashboard shows under "WEB" group namespace = "workers" # dashboard shows under "WORKERS" group namespace = "default" # (default if omitted) ``` --- ## Windows Path Examples ```toml # API server [[apps]] name = "api" script = "python" args = ["-m", "uvicorn", "app.main:app", "--reload"] cwd = "C:/Users/me/projects/api" namespace = "web" [apps.env] PYTHONPATH = "." # Node backend [[apps]] name = "node-api" script = "node" args = ["dist/index.js"] cwd = "C:/Users/me/projects/node-api" namespace = "web" [apps.env] NODE_ENV = "production" PORT = "3001" # .NET service [[apps]] name = "grpc-service" script = "dotnet" args = ["MyService.dll"] cwd = "C:/Users/me/projects/service/bin/Release/net8.0" namespace = "services" # Background worker (npm script) [[apps]] name = "queue-worker" script = "npm" args = ["run", "worker"] cwd = "C:/Users/me/projects/worker" namespace = "workers" autorestart = true max_restarts = 20 restart_delay_ms = 3000 [apps.env] NODE_ENV = "production" REDIS_URL = "redis://localhost:6379" ``` --- ## Loading the Config ```powershell # Start all apps alter start alter.config.toml # Also works alter start C:\projects\alter.config.toml alter start ./alter.config.json ``` > alter detects config files by their extension (`.toml` or `.json`). Any other value is treated as a script to run directly. After loading, each app appears as a separate process in `alter list` and the web dashboard, with its own logs, restart counter, and controls. --- ## .env File Support Use `env_file` to load environment variables from a `.env` file. Values are merged with explicit `env` — explicit `env` keys always win on conflict. ```toml [[apps]] name = "api" script = "python" args = ["-m", "uvicorn", "main:app"] cwd = "C:/projects/api" env_file = ".env" # relative to cwd, or absolute path [apps.env] PORT = "9000" # this overrides PORT from .env if both exist ``` The `.env` file uses standard `KEY=VALUE` format with comment support: ```dotenv DATABASE_URL=postgres://localhost/mydb SECRET_KEY=supersecret PORT=8000 # This is a comment DEBUG=false ``` --- ## Health Checks Use `health_check_url` to probe your process after it starts. Supports HTTP/HTTPS (checks for 2xx) and raw TCP (checks for successful connection). ```toml [[apps]] name = "api" script = "python" args = ["-m", "uvicorn", "main:app", "--port", "8000"] health_check_url = "http://localhost:8000/health" health_check_interval_secs = 30 # probe every 30s (default) health_check_timeout_secs = 5 # timeout per probe (default) health_check_retries = 3 # failures before marking unhealthy (default) ``` TCP probe example (useful for databases, Redis, etc.): ```toml health_check_url = "localhost:5432" # just host:port for TCP ``` When `health_check_retries` consecutive probes fail, the process is marked `unhealthy` and a notification is fired (if configured). The status recovers automatically when probes succeed again. --- ## Lifecycle Hooks Run shell commands at key lifecycle events. On Windows, hooks run via `cmd /C`. On Linux/macOS via `sh -c`. ```toml [[apps]] name = "api" script = "node" args = ["dist/index.js"] cwd = "C:/projects/api" pre_start = "npm run db:migrate" # blocks start — failure aborts launch post_start = "echo 'api is up' >> app.log" # non-blocking after process starts pre_stop = "npm run cleanup" # runs before process is killed ``` | Hook | When | Failure behaviour | |------|------|-------------------| | `pre_start` | Before spawning the process | **Aborts** the start — process is not launched | | `post_start` | After process reaches `running` state | Logged as a warning, process keeps running | | `pre_stop` | Before killing the process | Logged as a warning, process is killed anyway | --- ## Notifications Use the `notify` field to override notification settings for a specific process. It takes priority over namespace-level and global notification configs. ```toml [[apps]] name = "api" script = "python" args = ["-m", "uvicorn", "main:app"] namespace = "web" [apps.notify.slack] webhook_url = "https://hooks.slack.com/services/..." enabled = true channel = "#api-alerts" [apps.notify.events] on_crash = true on_restart = true on_start = false on_stop = false ``` You can also use a generic webhook: ```toml [apps.notify.webhook] url = "https://your-service.example.com/alter-hook" enabled = true [apps.notify.events] on_crash = true ``` > **Note:** If `notify` is omitted on a process, the namespace config applies. If the namespace has no config, the global config applies. Configure global and namespace defaults via the REST API — see [Notification Endpoints](./API.md#notification-endpoints). --- ## Tips **Naming conventions:** - Keep names short and lowercase: `api`, `web`, `worker`, `scheduler` - Names must be unique — duplicate names will overwrite each other **Iterating quickly:** - Edit the config file, then `alter start alter.config.toml` again - Already-running processes with the same name will be updated and restarted **Organizing large projects:** ```toml # Group everything by role using namespaces [[apps]] namespace = "web" # api, frontend, proxy [[apps]] namespace = "workers" # queue consumers, schedulers [[apps]] namespace = "infra" # redis proxy, health checks ```