Skip to main content

StackPilot

CI PyPI version Python versions License: MIT Typed

Local microservice orchestration without Docker.

StackPilot starts your services in dependency order, streams logs in one terminal, hot-reloads on file changes, and tracks actionable issues — all from a single Stackfile.py.

pip install stackpilot
cd my-project
stackpilot sync
stackpilot run

Features

  • One config file — declare services and external dependencies in Stackfile.py
  • Auto-discoverystackpilot sync detects FastAPI, Flask, Django, Celery, Express, NestJS, Postgres, Redis, MongoDB, RabbitMQ, and more
  • Apps vs infrastructure — only application services are started; Postgres/Redis are validated as external dependencies
  • Dependency-aware startup — independent services start in parallel waves; dependents wait for healthy dependencies
  • Health checks — HTTP, TCP, or process liveness before dependents launch
  • Hot reload — file changes restart only the affected service
  • Issue tracker — actionable crashes under .stackpilot/issues/, not endless log files
  • Doctor — environment, runtime integrity, ports, env files, and config diagnostics in one command
  • No Docker required for app processes (compose still works for databases)

Installation

Requires Python 3.10+.

pip install stackpilot

Verify:

stackpilot version
# or, if Scripts is not on PATH:
python -m stackpilot version

From source (contributors)

git clone https://github.com/subashsekar/stackpilot.git
cd stackpilot
python -m pip install -e ".[dev]"
pytest

Quick Start

stackpilot init          # create Stackfile.py
# edit services, or:
stackpilot sync          # auto-discover nested services
stackpilot run           # start + stream logs (Ctrl+C to stop)
stackpilot stop          # terminate a leftover session
stackpilot status        # runtime table (PID / port / uptime)

You can also execute the Stackfile directly:

python Stackfile.py

Commands walk up from the current directory until they find Stackfile.py (like Git finding .git). Runtime artifacts always land under that project root in .stackpilot/.


Commands

The public CLI for v0.1.x is frozen until v0.2.0.

Ten commands. One job each.

Command Definition
init Create a starter Stackfile.py
sync Discover nested services and write Stackfile.py
run Start services in dependency order and stream live logs
stop Terminate leftover processes from .stackpilot/runtime.json
graph Print a professional architecture dependency graph
status Show runtime status (PID, port, uptime, health)
ps List active StackPilot processes
issues List actionable service issues from .stackpilot/issues/
doctor Diagnose environment, Stackfile, and service configuration
version Print the installed StackPilot version
stackpilot --help
stackpilot <command> --help

stackpilot init

Definition: Create a starter Stackfile.py in the current directory.

Use this when you are starting a new project and do not have a Stackfile yet. It writes an empty template you can edit by hand, or overwrite later with stackpilot sync.

stackpilot init
stackpilot init --force    # overwrite an existing Stackfile.py
Option Definition
--force Overwrite an existing Stackfile.py without prompting

stackpilot sync

Definition: Walk nested directories, detect frameworks and infrastructure, and generate (or overwrite) Stackfile.py.

Use this to bootstrap or refresh configuration from the project layout. Application services become stack.service(...). Postgres/Redis become stack.external_dependency(...) and are never given a start command.

stackpilot sync
stackpilot sync --force    # overwrite Stackfile.py without prompting
Option Definition
--force Overwrite an existing Stackfile.py without prompting

Soft validation warnings (for example FastAPI without uvicorn) never abort sync.


stackpilot run

Definition: Validate external dependencies, start application services in dependency order, stream live logs, and hot-reload changed services until you press Ctrl+C.

stackpilot run             # start the full stack
stackpilot run auth        # start auth and its dependencies only
stackpilot run --force     # clear a stale session, then start
Argument / Option Definition
SERVICE (optional) Service name to start, plus everything it depends on
--force Stop leftover processes / clear stale runtime.json, then start

What it does:

  1. Detects a stale prior session (live PIDs or occupied StackPilot ports) and asks you to run stackpilot stop (or pass --force)
  2. Checks required external dependencies (Postgres/Redis/MongoDB/RabbitMQ) with retry + timeout
  3. Starts apps in topological order and waits for health checks
  4. Streams INFO / WARNING / ERROR lines to the terminal
  5. Watches files and reloads affected services when enabled
  6. On Ctrl+C: disable reload → stop process trees → stop watchers → exit 130

Use status / ps for runtime tables and issues for persisted problems — run itself only starts services and streams logs.


stackpilot stop

Definition: Terminate every service recorded in .stackpilot/runtime.json.

Use this when a previous stackpilot run did not shut down cleanly (closed terminal, crash, etc.) and ports / processes are still held.

stackpilot stop

What it does:

  1. Reads .stackpilot/runtime.json
  2. Stops each recorded process tree (Windows Job Objects / taskkill /T; Linux/macOS process groups)
  3. Ignores already-dead PIDs and clears stale runtime entries
  4. Prints a short summary (Stopping auth...✓ N services stopped.)

If there is no runtime file (and no Stackfile project): No running StackPilot session.
Corrupted runtime files are cleared without a traceback. stop does not require a Stackfile.py when a leftover .stackpilot/runtime.json is still present.


stackpilot graph

Definition: Print a professional architecture visualization of application services and external dependencies from the Stackfile.

Use this to understand startup order and depends_on relationships without starting anything. Output includes the dependency tree, external services, dependency depth, topological startup order, live status colors when a session is active, ports, detected frameworks, and cycle highlighting. ASCII fallback is used automatically on cp1252 / limited terminals.

stackpilot graph

Example output:

StackPilot Architecture
────────────────────────────────────────────

Services : 3
Running  : 0
Stopped  : 2
FastAPI  : 2
Django   : 0
Flask    : 0
Node     : 0
External : 1

🔴 gateway (:8000) [FastAPI]
└── 🔴 auth (:8001) [FastAPI]
    └── 🔵 PostgreSQL (:5432)

────────────────────────────────────────────

Total Services         : 2
Dependency Depth       : 2
Circular Dependencies  : None

Graph Generated Successfully

External nodes use infrastructure display names (for example PostgreSQL) and are never started by StackPilot. Circular dependencies abort with a clear cycle path.


stackpilot status

Definition: Show a runtime status report for the project: applications and external dependencies, including PID, port, uptime, and health when a session is active.

Reads .stackpilot/runtime.json written by a live stackpilot run session (works from another terminal). Safe to run when nothing is running — services simply show as stopped / inactive.

stackpilot status

Shows:

  • Project name and whether a run session is active
  • Applications — name, status, PID, port, uptime, health
  • External Dependencies — reachability-oriented rows for Postgres/Redis, etc.

stackpilot ps

Definition: List active StackPilot processes in a concise table.

A shorter companion to status, focused on what is currently running (name, PID, port, state).

stackpilot ps

stackpilot issues

Definition: List actionable service problems stored under .stackpilot/issues/ (not full log files).

Default view is ACTIVE issues only. Use --fixed for recently fixed rows. Pass a service name to filter to that service.

stackpilot issues              # ACTIVE issues for all services
stackpilot issues --fixed      # recently FIXED issues
stackpilot issues auth         # every issue for service "auth"
stackpilot issues auth --fixed
Argument / option Definition
SERVICE (optional) Limit output to one known service name
--fixed Show recently fixed issues instead of ACTIVE ones

An empty issues/ directory means the project is healthy.


stackpilot doctor

Definition: Diagnose the environment, Stackfile, dependency graph, ports, health checks, runtime integrity, and external dependencies in one report.

Use this when sync/run fails, imports break, ports conflict, or Postgres/Redis look misconfigured. Exit code is non-zero when errors are present.

stackpilot doctor

Checks include: Python / package import, Stackfile load, service paths and commands (missing executables), duplicate/free ports, dependency cycles, health-check / URL validity, env files, runtime.json integrity, orphan processes, project permissions, and external dependency reachability.


stackpilot version

Definition: Print the installed StackPilot package version and exit.

stackpilot version
python -m stackpilot version

Command cheat sheet

stackpilot init
stackpilot sync
stackpilot run
stackpilot run auth
stackpilot stop
stackpilot graph
stackpilot status
stackpilot ps
stackpilot issues
stackpilot issues --fixed
stackpilot issues auth
stackpilot doctor
stackpilot version

If no Stackfile is found:

No Stackfile.py found.

Create one:

  stackpilot init
  stackpilot sync

Then start services:

  stackpilot run

First Project

1. Layout

my-project/
  auth/
    main.py          # FastAPI app
  gateway/
    main.py

2. Discover

cd my-project
stackpilot sync

3. Generated Stackfile.py

from stackpilot import Stack, HttpHealthCheck

stack = Stack()

stack.external_dependency(
    name="postgres",
    type="postgresql",
    host="127.0.0.1",
    port=5432,
)

stack.service(
    name="auth",
    path="./auth",
    command="python -m uvicorn main:app --reload --host 0.0.0.0 --port 8000",
    port=8000,
    health_check=HttpHealthCheck(url="http://127.0.0.1:8000/health"),
    depends_on=["postgres"],  # external deps are validated, never started
)

stack.service(
    name="gateway",
    path="./gateway",
    command="python -m uvicorn main:app --reload --host 0.0.0.0 --port 8001",
    port=8001,
    health_check=HttpHealthCheck(url="http://127.0.0.1:8001/health"),
    depends_on=["auth"],   # add application dependencies by hand when needed
)

stack.run()

StackPilot starts application services only. PostgreSQL, Redis, and other infrastructure are declared as external_dependency entries: they appear in graph / status / doctor, are TCP-validated before startup, and are never spawned by Runner.

4. Run

stackpilot run

In another terminal:

stackpilot status
stackpilot ps
stackpilot issues

Ready-made samples live under examples/. Also see examples/external-deps/ for a Postgres + Redis layout.


External Dependencies

StackPilot distinguishes application services from external dependencies.

Application Service External Dependency
Declared with stack.service(...) stack.external_dependency(...)
Has a process command Yes No
Started by Runner / ProcessManager Yes Never
Validated before run Health check after start TCP reachability with retry + timeout before any app starts
Shown in Applications (status), graph External Dependencies (status), graph as infrastructure labels

Supported infrastructure

Type Default port Validation
PostgreSQL (postgresql / postgres) 5432 TCP connect via the Health Engine
Redis (redis) 6379 TCP connect via the Health Engine
MongoDB (mongodb / mongo) 27017 TCP connect via the Health Engine
RabbitMQ (rabbitmq / amqp) 5672 TCP connect via the Health Engine

stackpilot sync detects these directories / compose images and emits stack.external_dependency(...) — never an executable stack.service(...).

How validation works

Before application startup, StackPilot probes each required external dependency through the Health Engine (TCP preferred on the declared host/port).

Defaults (overridable per dependency):

Setting Default Meaning
retries 5 Maximum probe attempts
retry_delay 0.5s Delay between attempts
retry_backoff fixed fixed or exponential
health-check timeout 10s Hard deadline for the whole wait
stack.external_dependency(
    name="postgres",
    type="postgresql",
    host="127.0.0.1",
    port=5432,
    retries=5,
    retry_delay=0.5,
    retry_backoff="exponential",  # or "fixed"
)

All reachable:

Checking external dependencies...

Checking PostgreSQL...
Attempt 1/5...
Connected.
✓ PostgreSQL (127.0.0.1:5432)

Checking Redis...
Attempt 1/5...
Connected.
✓ Redis (127.0.0.1:6379)

Starting application services...

One unavailable (retries exhausted):

Checking external dependencies...

Checking PostgreSQL...
Attempt 1/5...
Attempt 2/5...
Attempt 3/5...
Attempt 4/5...
Attempt 5/5...
✗ PostgreSQL is not reachable.

Problem: Dependency unavailable
Dependency: PostgreSQL
Host: 127.0.0.1
Port: 5432
Elapsed: 10.0s
Attempts: 5/5

Services depending on PostgreSQL:
- auth
- users

Suggested fix: Start PostgreSQL (or update host/port in Stackfile.py), then re-run `stackpilot run`. Verify with `stackpilot doctor`.

Startup aborted.

No application processes are started when validation fails.


Framework Support

stackpilot sync walks nested directories and asks the adapter registry which framework matches. Soft validation warnings (for example FastAPI without uvicorn) never abort sync.

Framework Detection signals Generated command Health
NestJS package.json + @nestjs/core <pm> run start:dev HTTP /health when exposed, else TCP
Express package.json + express <pm> run dev HTTP /
Django manage.py + settings / WSGI / ASGI python manage.py runserver HTTP /
Celery Celery() / worker modules celery -A <app> worker PROCESS
FastAPI FastAPI() / common layouts python -m uvicorn <module>:<attr> --reload HTTP /health
Flask Flask() / create_app() flask --app … run --host 0.0.0.0 --port N HTTP /
PostgreSQL compose / postgresql.conf external_dependency (TCP 5432) — never started TCP
MongoDB compose / mongod.conf / mongodb:// external_dependency (TCP 27017) — never started TCP
RabbitMQ compose / amqp:// / rabbitmq:3 external_dependency (TCP 5672) — never started TCP
Redis redis.conf / compose external_dependency (TCP 6379) — never started TCP
Generic main.py / app.py / bare package.json python … or <pm> start PROCESS

See examples/ for minimal projects per framework.

Package managers

Python: uv → Poetry → Pipenv → pip (with local .venv when present)

Node: bun → pnpm → yarn → npm (lockfile wins)

Port detection

  1. .env / .env.* keys (PORT, APP_PORT, …)
  2. Compose host port mappings
  3. Sequential defaults from 8000 (Postgres 5432, Redis 6379, MongoDB 27017, RabbitMQ 5672)

When a preferred Flask port is already taken by another discovered service, StackPilot assigns the next free coordination port and generates flask run --host 0.0.0.0 --port {assigned} so the process actually listens on the Stackfile port (not a hardcoded app.run(port=…)).

Custom adapters

Add one adapter file and register it in the adapter registry — nothing else. Third-party code can also build a private AdapterRegistry.


Health Checks

Prefer typed models exported from stackpilot:

from stackpilot import ProcessHealthCheck, HttpHealthCheck, TcpHealthCheck

HttpHealthCheck(url="http://127.0.0.1:8000/health")
TcpHealthCheck(host="127.0.0.1", port=5432)
ProcessHealthCheck()  # process must stay alive

Legacy dict configs ({"type": "http", "url": "..."}) remain supported.

Dependents wait until a service passes its health check (or the process stays alive for process health). Failures surface in the Issue Tracker.

External dependencies default to a TCP probe on their declared host/port and are validated before any application service starts.


Issue Tracking

StackPilot does not persist normal service logs. Live INFO / WARNING / ERROR lines still stream in the stackpilot run terminal — only actionable problems are written under .stackpilot/issues/.

Each service owns one compact table file (for example auth.issue). Python tracebacks are reduced to error message + project-relative path:line. Duplicate ACTIVE rows are ignored.

ACTIVE
  ↓  (issue resolved / service healthy)
FIXED
  ↓  (1 hour)
Row removed
  ↓  (no rows left)
Delete <service>.issue

An empty issues/ folder means the project is healthy.

stackpilot issues           # ACTIVE
stackpilot issues --fixed   # recently fixed
stackpilot issues auth      # one service

FAQ

Do I need Docker?
No. StackPilot only starts application processes. Postgres/Redis are external dependencies — start them yourself (Docker, local install, managed DB, …); StackPilot only checks that they are reachable before launching apps.

Where is the config file?
Always Stackfile.py — never stackpilot.py (that name would shadow the package).

Is Stackfile.py trusted code?
Yes. Loading a Stackfile can execute arbitrary Python, and command= values are spawned as your user. Review Stackfiles like you would a Makefile. See SECURITY.md.

Can I run from a subdirectory?
Yes. Discovery walks parents like Git. Artifacts stay under the project root.

How do I stop everything?
Prefer Ctrl+C in the active stackpilot run terminal. To clean up after a crash or closed terminal, run stackpilot stop — it reads runtime.json, stops process trees, and clears stale entries. If stackpilot run reports an existing session, run stackpilot stop or stackpilot run --force.

What happens during shutdown?
disable reload → stop processes → stop watchers → unbind → logger shutdown. A shutdown summary lists each stopped service. Orphan children should not remain.

What about restart / hot reload?
While run is active, file watchers restart changed services (when reload=True, or when Windows takes over uvicorn/Django/Flask-debug native reload). Debounced callbacks are ignored once Ctrl+C begins shutdown. Limitations:

  • Generator defaults do not set reload=True; FastAPI often relies on uvicorn --reload instead.
  • On Windows, StackPilot strips uvicorn --reload / Django auto-reload / Flask --debug and owns restart so CTRL_C_EVENT cannot tear down the whole stack.
  • Reload restarts only the changed service (plus restart_dependents when set).

Where did stackpilot logs go?
Live logs still stream in the run terminal. Persistence is the Issue Tracker (.stackpilot/issues/) via stackpilot issues — not .stackpilot/logs/.

What if a service fails to spawn?
Missing executables, missing directories, permission errors, invalid commands, and port conflicts print a short Problem / Affected service / Reason / Suggested fix block without a raw traceback. Run stackpilot doctor for deeper checks.

What if Postgres/Redis/MongoDB/RabbitMQ is down?
External dependency validation retries (default 5 attempts with delay/backoff) until the configured timeout, then aborts before starting application processes. The message lists host, port, elapsed time, attempts, dependents, and the next action.

How do I check health without reading logs?
stackpilot status, stackpilot doctor, and stackpilot issues.


Known Limitations

  • StackPilot starts application processes only. Databases and brokers must already be running (or reachable) as external dependencies.
  • Generator defaults do not set reload=True; many frameworks rely on their own reload flags (uvicorn --reload, etc.).
  • On Windows, StackPilot may strip native uvicorn/Django/Flask-debug reload and own restarts so CTRL_C_EVENT cannot tear down the whole stack.
  • Hot reload restarts the changed service (plus restart_dependents when set), not the entire graph.
  • stackpilot stop cleans recorded runtime PIDs; processes started outside StackPilot are not managed.
  • Stackfile.py is trusted code (see SECURITY.md).

Troubleshooting

Symptom What to try
No Stackfile.py found stackpilot init or stackpilot sync from the project root
Sync finds nothing Put each service in a nested directory (the project root itself is never a service)
Existing StackPilot session detected stackpilot stop or stackpilot run --force
Corrupted runtime status stackpilot stop (clears .stackpilot/runtime.json) then re-run
Port already in use stackpilot stop, change port= / health URL, or free the port
Executable not found Activate the project venv or fix command= — then stackpilot doctor
Permission denied Check execute bits / antivirus locks on the service path
Invalid cwd / bad path Fix path= so it exists under the project root
Dependency unavailable Start Postgres/Redis/MongoDB/RabbitMQ (or fix host/port); wait for boot; re-run
Health endpoint missing Confirm the route exists and matches health_check=
Health timeout Check the run terminal, stackpilot issues <name>, then doctor
Service fails on start stackpilot issues <name> and open the referenced FILE:LINE
Flask / Werkzeug banners look loud Startup lines like * Serving Flask app are INFO, not ERROR
Import / CLI missing pip install stackpilot then python -m stackpilot doctor
Wrong Python / venv Activate the project venv, or use uv/Poetry/Pipenv so sync emits the right runner
Stackfile load / config error stackpilot doctor — check syntax and that stack = Stack() exists
Ctrl+C leaves orphans Upgrade to latest 0.1.x; report if process trees remain after shutdown summary
stackpilot doctor

Contributing

See CONTRIBUTING.md for setup, tests, and the frozen CLI / architecture rules for v0.1.x.

python -m pip install -e ".[dev]"
pytest

Security

See SECURITY.md for the trusted-Stackfile threat model, supported versions, and how to report vulnerabilities.


License

MIT © StackPilot contributors

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stackpilot-0.1.0.tar.gz (334.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stackpilot-0.1.0-py3-none-any.whl (222.6 kB view details)

Uploaded Python 3

File details

Details for the file stackpilot-0.1.0.tar.gz.

File metadata

  • Download URL: stackpilot-0.1.0.tar.gz
  • Upload date:
  • Size: 334.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for stackpilot-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9089f93c60e707679c6f635a027e12b495eb6ab0962b6e6b496831a409d0c9ea
MD5 eaea43fcc173347700b5e529c5b8173b
BLAKE2b-256 9c2225e5046079a0370fc4c4c67fe46f477b93f59337fe0574f895ba009ac147

See more details on using hashes here.

Provenance

The following attestation bundles were made for stackpilot-0.1.0.tar.gz:

Publisher: publish.yml on subashsekar/stackpilot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stackpilot-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: stackpilot-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 222.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for stackpilot-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b72b5600ac6c3f14a981e981890e49314befc3dfe010bea125495d1bfd5925ac
MD5 d503a5d7b4cb8437e56acedcb392fe58
BLAKE2b-256 07858f11ac5e5af9777ee1a3cfecd4b0ea348ab703cc540a77729d0aadd9924f

See more details on using hashes here.

Provenance

The following attestation bundles were made for stackpilot-0.1.0-py3-none-any.whl:

Publisher: publish.yml on subashsekar/stackpilot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page