Skip to main content
Alpha

Deploy locally with Aspire

Goal: run your whole NetScript workspace on one machine under .NET Aspire — scaffold the AppHost, bring up the resource graph (Postgres, Redis, every service and background processor), and watch it from the Aspire dashboard. This is the local orchestration companion to the Deploy recipe (which covers shipping to a remote target); for why the AppHost works the way it does, read Orchestration with Aspire.

Prerequisites

What you need before you start
NameTypeDescription
A scaffolded workspace netscript init Created WITHOUT --no-aspire, so the aspire/ AppHost folder exists. See the CLI reference for init flags.
Docker daemon running container engine Aspire provisions Postgres and Redis as local Docker containers. No daemon = the default workflow does not start.
The Aspire CLI aspire (external) The aspire restore / aspire start commands are the external .NET Aspire CLI, run from inside aspire/ — not netscript subcommands.
A reachable port range ports Dashboard :18888 (HTTPS) / :18889 (HTTP), OTLP :4318, services from :3000, plugin APIs :8091–8099, the Fresh app :8010.

Step 1 — Confirm the AppHost was scaffolded

The AppHost is a small TypeScript/Node program (not C#) at aspire/apphost.mts, configured by aspire/aspire.config.json. netscript init generates it; you do not hand-write it. Verify it is on disk before going further:

# From the workspace root — these two files are the orchestration entry point.
ls aspire/apphost.mts aspire/aspire.config.json

aspire.config.json pins language: "typescript/nodejs", appHost.path: "apphost.mts", and the Aspire SDK version 13.4.6. The graph inside the AppHost is derived from your installed plugins at boot via composeAppHost (from @netscript/aspire/application) — add a plugin and its API plus background processor appear in the graph; remove it and they vanish, no edit to apphost.mts required. The mechanics are in Orchestration with Aspire.

Step 2 — Restore the AppHost SDK (once)

The AppHost runs on its own isolated Node runtime inside aspire/ so its dependency graph never leaks into your Deno workspace. Restore that runtime once per machine (and after an SDK bump):

# Run from inside the aspire/ folder. One-time SDK restore.
cd aspire
aspire restore

Step 3 — Start the resource graph

A single aspire start translates appsettings.json plus the plugin contributions into a coherent resource graph and boots all of it — infrastructure first, then services, plugin APIs, and background processors, with cross-references resolved into injected environment variables:

# Still inside aspire/. Boots the whole graph; prints the dashboard URL + a login token.
aspire start

When boot finishes, aspire start prints the dashboard address and a one-time login token. The graph a single run stands up:

What aspire start brings up (the local resource graph)
NameTypeDescription
aspire (dashboard) https://localhost:18888 / http://localhost:18889 The Aspire dashboard. Live resource list, console logs, structured logs and traces. A login token is printed on start.
OTLP collector http://localhost:4318 OpenTelemetry endpoint (http/protobuf) the dashboard runs; framework spans and structured logs land here automatically.
postgres Container Provisioned via Docker. The database netscript db commands target — reachable only once Aspire is up. Postgres is the default; pass --db mysql or --db mssql at init for those engines (each also an Aspire container), or --db sqlite for a file-backed database with no container.
redis Container (cache) Redis cache — the default --cache-backend; Redis-compatible. Backs KV/queue workloads for the runtime plugins. Swap it for garnet (also a container) or app-level deno-kv via --cache-backend.
users (example service) :3000+ (SERVICE range, OS-allocated from 3000) The scaffolded oRPC service, when you init with --service. OpenAPI at /api/v1/users/* and RPC at /api/rpc/*.
plugin APIs :8091–8099 (PLUGIN_API range) Each installed runtime plugin's HTTP API (workers, sagas, triggers, auth). Ports are allocated from the plugin range, not hardcoded.
background processors executables (no port) Each plugin's isolated runners (workers, sagas, triggers) — separate processes, not threads inside the API.
dashboard (Fresh app) :8010 The scaffolded Fresh frontend, when present (the app range start 8000 + 10).

Step 4 — Initialize the database (through the running AppHost)

With Aspire up, your database engine is live and the netscript db commands can reach it. The resource graph above shows Postgres — the default and the engine these recipes use; if you scaffolded with --db mysql / --db mssql you get that container instead, and --db sqlite gives a file-backed database with no Aspire container. Run the commands from the workspace root (a second terminal — leave aspire start running in the first):

# From the workspace root, with `aspire start` still up in another terminal.
netscript db init --name init   # create + apply the first migration
netscript db generate           # generate the Prisma client
netscript db seed               # optional: seed development data

These talk to the Postgres container Aspire provisioned. Outside Aspire (an isolated CI container, say), point them at your own database via POSTGRES_URI / DATABASE_URL instead — covered in

Database & migration .

Step 5 — Use the dashboard

Open https://localhost:18888, paste the login token aspire start printed, and you have a single pane over the running graph:

Aspire dashboard surfaces
NameTypeDescription
Resources tab Every container and executable above with status, endpoints, and the resolved environment. The authority for which port each resource bound.
Console logs tab stdout/stderr per resource — a failing background processor is one click away, not buried in a terminal.
Structured logs + Traces tab Spans and structured logs your handlers emit, correlated by traceparent across services. Aspire collects them via the OTLP endpoint at :4318.

Because Aspire starts each resource with its OTEL_SERVICE_NAME and an OTLP endpoint pointed at http://localhost:4318, framework-level spans (job dispatch, job execution, scheduler runs) surface here with no extra wiring. See Observability

for the framework-vs-scaffold span boundary.

In-production pitfalls

See also

  • Why it works this way: Orchestration with Aspire

    — the AppHost, plugin contributions, two-pass reference resolution, and the dashboard.

  • Ship it remotely: Deploy — the production companion: deployable units, backing services, and the --no-aspire path.

  • The database sequence: Database and

Database & migration .