{"id":30887772,"url":"https://github.com/yungvldai/maestro","last_synced_at":"2026-03-16T18:31:46.675Z","repository":{"id":212384962,"uuid":"729365809","full_name":"yungvldai/maestro","owner":"yungvldai","description":"Simple process manager 🎻","archived":false,"fork":false,"pushed_at":"2024-04-14T14:33:58.000Z","size":1263,"stargazers_count":20,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-31T07:28:00.937Z","etag":null,"topics":["docker","linux","manager","process"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yungvldai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-12-09T02:32:13.000Z","updated_at":"2024-08-22T15:53:28.000Z","dependencies_parsed_at":"2024-01-14T11:05:26.872Z","dependency_job_id":null,"html_url":"https://github.com/yungvldai/maestro","commit_stats":null,"previous_names":["yungvldai/maestro"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/yungvldai/maestro","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yungvldai%2Fmaestro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yungvldai%2Fmaestro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yungvldai%2Fmaestro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yungvldai%2Fmaestro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yungvldai","download_url":"https://codeload.github.com/yungvldai/maestro/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yungvldai%2Fmaestro/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274196122,"owners_count":25239148,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","linux","manager","process"],"created_at":"2025-09-08T14:10:24.590Z","updated_at":"2026-03-16T18:31:46.646Z","avatar_url":"https://github.com/yungvldai.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# maestro\n\n![maestro](https://github.com/yungvldai/maestro/blob/main/media/cover.png)\n\n`maestro` is a simple process manager and it can help you start multiple processes dependent on each other and then stop them in the right order. \n\nOne of the use cases could be running in Docker. Although it is generally an anti-pattern to run multiple services in one container, sometimes it is a necessary requirement.\n\n\u003c!-- TOC tocDepth:2..5 chapterDepth:2..6 --\u003e\n\n- [Features](#features)\n- [Installation](#installation)\n- [Operation](#operation)\n- [Configuration](#configuration)\n  - [`pid`](#pid)\n  - [`log_level`](#log_level)\n  - [`apps`](#apps)\n    - [`stdout` \u0026 `stderr`](#stdout-stderr)\n    - [`signal`](#signal)\n    - [`user`](#user)\n    - [`depends_on`](#depends_on)\n    - [`ready`](#ready)\n      - [`exit_code`](#exit_code)\n      - [`delay`](#delay)\n      - [`command`](#command)\n      - [`http`](#http)\n- [Recipes](#recipes)\n  - [Using in Docker](#using-in-docker)\n  - [Using environment variables in config](#using-environment-variables-in-config)\n- [Known issues](#known-issues)\n\n\u003c!-- /TOC --\u003e\n\n## Features\n\n- Running processes under different users\n- The correct order to start and stop processes\n- 4 types of process readiness probe: `delay`, `http`, `command` and `exit_code`\n- Ability to specify a stop signal separately for each process\n- Redirecting of stdout and stderr\n- Simple YAML configuration\n\n## Installation\n\nYou can check existing builds and versions [here](https://github.com/yungvldai/maestro/releases).\n\n```bash\nexport MAESTRO_VERSION=1.0.0 # Specify required version\nexport MAESTRO_BUILD=linux-musl # Specify required build\n\ncurl -o maestro.zip \"https://github.com/yungvldai/maestro/releases/download/${MAESTRO_VERSION}/maestro-${MAESTRO_BUILD}.zip\" \u0026\u0026 unzip maestro.zip \u0026\u0026 rm maestro.zip\n```\n\n## Operation\n\n`maestro` will start all apps in the specified order and will be listening for signals. If `maestro` receives the appropriate signal, it will attempt to gracefully stop the started apps in reverse order. If any of the app stop on their own, exiting with a non-zero code (or if `maestro` fails to obtain an exit code), `maestro` will also attempt to stop the remaining apps, preserving the order, and then exit itself.\n\nExiting the `maestro` program will only occur when all processes are either never started (**INIT**) or already **STOPPED** (excluding SIGKILL, of course).\n\n## Configuration\n\nThe configuration file `maestro.yml` must be placed either in the current working directory or in `/etc/maestro`.\nThe configuration file must be a valid YAML document.\n\n### `pid`\n\nYou can specify the `pid` option; in this case, when `maestro` starts, it will write the ID of the main process (itself) to the file whose path you provide.\n\nFor example:\n\n```yaml\npid: /var/run/maestro.pid\n```\n\nBy default, it only prints it. Note that the PID log has an info severity level `info` (you can read about loggging levels below).\n\n### `log_level`\n\n`maestro` supports various levels of logging, such as: `debug`, `info`, `warn` and `error`. With this option, you can configure the messages you want to see during operation. Also, it may be controlled using `RUST_LOG` environment variable.\n\n### `apps`\n\nApps must be an array. The app must have a `name` (any valid YAML string) and `command` (array of strings).\n\nExample of minimal app config:\n\n```yaml\napps:\n  - name: app\n    command: [\"node\", \"./app.js\"]\n```\n\n#### `stdout` \u0026 `stderr`\n\nYou can redirect stdout and stderr to a file, to your terminal, or completely mute them. To do this, use the `stdout` and `stderr` options.\n\nFor redirecting to a file, the option should take a value - the path to the file. Ensure that the user who running the `maestro` has the capabiltiy to write to this file. Do not worry, `maestro` will create nested directories automatically.\n\nExample:\n\n```yaml\napps:\n  - name: app\n    stdout: /var/log/maestro/app/stdout.log\n```\n\nTo display messages in the terminal from which `maestro` was launched, pass the keyword `inherit`:\n\n```yaml\napps:\n  - name: app\n    stderr: inherit\n```\n\nBy default, app logs are not written anywhere.\n\n#### `signal`\n\nWhen `maestro` receives SIGINT (2) or SIGTERM (15), it initiates the shutdown procedure. All apps are stopped in the order dictated by `depends_on`. Although `maestro` itself only handles SIGINT and SIGTERM, you can specify the signal that should be sent to the app for shutdown.\n\nThis can be a numeric signal identifier or one of the strings: `sigint`, `sigterm`, `int`, `term`, in any case. By default, `maestro` will send a SIGTERM to your app.\n\n`maestro` will wait for all your apps to stop until it receives a SIGKILL itself. `maestro` will attempt to send a SIGKILL to your app if an error occurs when attempting to send the specified signal.\n\n#### `user`\n\nBy default, all your apps will run under the current effective user id. However, you can change this behavior by providing the `user` option. You can pass a username (in this case, the `id` command must be supported in your OS), or directly provide a uid.\n\nUnder the hood, `maestro` will call setuid in the child process (app), so this mechanism has limitations. For example, if you run `maestro` without root privileges, you can only start other app under the same user as `maestro` itself (this information requires confirmation).\n\n#### `depends_on`\n\n`depends_on` allows you to specify apps that must be **READY** before the configured app starts. The readiness of an app is determined by the readiness probe (option `ready`, read below).\n\n`depends_on` takes an array of strings - the names of other apps.\n\nExample:\n\n```yaml\napps:\n  - name: db\n    command: [\"postgres\"]\n    ready: \n      command: [\"./ping-postgres.sh\"] # Pings postgres and exits with 0 or 1 code\n  - name: server\n    command: [\"python\", \"server.py\"]\n    depends_on:\n      - db\n```\n\nIn the example above, the `server` won't be started until the `ping-postgres.sh` script exits with a code of 0.\n\nWhen stopping `maestro`, it will wait for all dependents to stop before proceeding to stop the app.\n\n#### `ready`\n\nBy default, apps are considered ready immediately after start. This can be changed by configuring a readiness probe using this option.\n\n##### `exit_code`\n\nThe app will be considered **READY** if it exits with the specified code. This is useful if you need to run a script before the app.\n\nExample: \n\n```yaml\napps:\n  - name: migrations\n    command: [\"./run-migrations\"]\n    ready: \n      exit_code: 0\n  - name: server\n    command: [\"python\", \"server.py\"]\n    depends_on:\n      - migrations\n```\n\n##### `delay`\n\nThe app will be considered **READY** after the specified number of milliseconds following its launch.\n\n```yaml\napps:\n  - name: app\n    command: [\"node\", \"app.js\"]\n    ready:\n      delay: 5000\n```\n\n##### `command`\n\n`maestro` will execute the specified command at the specified interval (default: 1s). The app will be considered **READY** if the command returns a zero exit code at some point. Please note that the command execution is blocking.\n\nExample: \n\n```yaml\napps:\n  - name: app\n    command: [\"node\", \"app.js\"]\n    ready:\n      command: [\"npm\", \"run\", \"check-ready\"]\n      period: 1000 # may be omitted (default: 1000ms)\n```\n\n##### `http`\n\n`maestro` will make the specified HTTP request at the specified interval (default: 1s). The app will be considered **READY** if a 2xx HTTP status is returned in response. Please note that the request is blocking, and a timeout of 1 second is set for it.\n\nExample: \n\n```yaml\napps:\n  - name: app\n    command: [\"node\", \"app.js\", \"--\", \"--port\", \"3000\"]\n    ready:\n      url: http://localhost:3000/health-check\n      method: GET # case-insensitive and may be omitted (default: GET)\n      period: 1000 # may be omitted (default: 1000ms)\n```\n\n## Recipes\n\n### Using in Docker\n\n`maestro.yml`:\n\n```yml\napps:\n  - name: server\n    command: [\"./run.sh\"]\n    ready:\n      url: http://localhost:3000/health-check\n  - name: gateway\n    command: [\"nginx\", \"-g\", \"daemon off;\"]\n    depends_on:\n      server\n```\n\n`Dockerfile`:\n\n```Dockerfile\n...\n\n# Example platform \u0026 version\nENV MAESTRO_VERSION 1.0.3\nENV MAESTRO_BUILD linux-musl\n\nRUN wget \"https://github.com/yungvldai/maestro/releases/download/${MAESTRO_VERSION}/maestro-${MAESTRO_BUILD}.zip\" \u0026\u0026 \\\n\tunzip \"maestro-${MAESTRO_BUILD}.zip\" \u0026\u0026 \\\n\trm \"maestro-${MAESTRO_BUILD}.zip\" \u0026\u0026 \\\n\tmv \"maestro-${MAESTRO_BUILD}\" /usr/local/bin/maestro \u0026\u0026 \\\n\tchmod a+x /usr/local/bin/maestro \u0026\u0026 \\\n\tmkdir -p /etc/maestro\n\nCOPY ./maestro.yml /etc/maestro/maestro.yml\n\nCMD [\"maestro\"]\n```\n\n### Using environment variables in config\n\nYou can use [envsubst](https://linux.die.net/man/1/envsubst) to substitute environment variables into the config.\n\n`maestro-template.yml`:\n\n```yml\napps:\n  - name: server\n    command: [\"./run.sh\"]\n    stdout: $LOGS_DIR/stdout.log # It will be /var/logs/stdout.log\n    ready:\n      url: http://localhost:3000/health-check\n```\n\n\n`run.sh`:\n\n```bash\n#!/bin/sh\n\nset -e\n\nexport LOGS_DIR=\"/var/logs\"\n\nenvsubst '$LOGS_DIR' \u003c \"./maestro-template.yml\" \u003e \"./maestro.yml\"\n```\n\n## Known issues\n\n- Signals generated by keyboard interrupts (like Ctrl+C) are sent to every process in the foreground [process group](https://en.wikipedia.org/wiki/Process_group) of the current session. Therefore `maestro` cannot guarantee the correct order to stop apps when using signals generated by keyboard. Therefore, if you want to manually stop `maestro`, then your best bet is to call kill with maestro's PID in an adjacent terminal:\n```bash\nkill -15 \u003cmaestro pid\u003e\n```\n\n- Only zero exit codes supported in `ready`.`exit_code` readiness probe, because when receiving a non-zero code from any process, the maestro will stop all other processes and exit itself.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyungvldai%2Fmaestro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyungvldai%2Fmaestro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyungvldai%2Fmaestro/lists"}