The deployment lifecycle
Every deployment follows the same arc: compile the code, prepare the environment, then run the app. Launchfile gives you a hook for each stage via the commands block.
Here's the order, and when each command fires:
build— Compile source, install dependencies, generate assets. Runs at build time.release— Runs once per deployment, before start. Migrations, asset uploads, cache warming. If it fails, the deploy stops.start— The long-running process. This is your app.seed— Populate the database with sample or default data. Typically run on demand, not on every deploy.test— Run the test suite. Used by CI and during deploys that validate before promoting.bootstrap— First-time setup. Create the admin user, generate invite links, initialize config. Runs on user request, not automatically.
Think of release as "every deploy" (migrations) and bootstrap as "first deploy ever" (admin setup). Both run against a live app, but release gates the deploy while bootstrap is fire-and-forget.
Build it up
start command — this is what launches your app.commands:
start: "node server.js"commands:
build: "npm install && npm run build"
release: "npx prisma migrate deploy"
start: "node dist/server.js"commands:
build: "npm install && npm run build"
release: "npx prisma migrate deploy"
start: "node dist/server.js"
seed: "node scripts/seed.js"
test: "npm test"commands:
start:
run: "my-app serve"
capture:
ADMIN_TOKEN: "Admin token: (.+)"The build block
There's a subtle but important distinction: commands.build runs shell commands (like npm run build), while a top-level build: block configures a Dockerfile-based container build. They serve different purposes.
commands:
build: "npm install && npm run build"
start: "node dist/server.js"build:
dockerfile: ./Dockerfile
commands:
start: "node server.js"commands.build runs inside an already-built container (or on the host). The top-level build: block describes how to build the container image itself from a Dockerfile. If you're using a prebuilt image (like image: node:20), you only need commands.build. If you have a Dockerfile, use the top-level build: block.
Running from source: install and dev
The commands above run the app as a built artifact — build compiles it, start runs it. But on a dev machine you often want to run the app from source instead: hot reload, the repo's own dev server, no container. That's a different execution mode, and it varies exactly one thing — the command lines.
Two source-mode keys pair with the artifact ones:
install— source-mode prepare (the counterpart ofbuild). Runs on demand: first launch, or when dependencies change.dev— source-mode run (the counterpart ofstart). Your dev server.
commands:
build: "bun install && bun run build"
start: "node dist/server.js"source: ./apps/api # working dir for install/dev
commands:
install: "bun install"
dev: "bun run dev"launchfile up runs the artifact (build → start); launchfile dev runs from source (install → dev), in the optional source directory. Run precedence is dev > image > start: a prebuilt image stays an artifact unless dev overrides it. Only prepare and run are mode-aware — release, bootstrap, seed, and test are the same command in either mode.
An app whose dev command equals its production command needs nothing extra — a plain start works in both modes. Add dev / install only when running from source needs a different command line than the built artifact (a compiled binary vs. bun run dev, an image entrypoint vs. a repo script).
In the wild
This spec example shows a real-world pattern: a post-start bootstrap command that creates an admin user and captures the one-time invite link from stdout.
version: launch/v1
name: my-app
build:
dockerfile: ./Dockerfile
provides:
- protocol: http
port: 9999
exposed: true
env:
APP_SECRET:
generator: secret
sensitive: true
PUBLIC_URL:
default: $app.url
description: "Public URL the app is reachable on (auth callbacks, email links)"
health: /health
restart: always
commands:
start: "node server.js"
# Post-start setup: create the first admin and capture the one-time invite link.
# Run by the user via `launchfile bootstrap my-app` (or the provider's equivalent)
# after the app is up. Re-runnable; failures are reported, not deploy-failing.
bootstrap:
command: "my-app-cli create-invite --name admin --url $app.url"
capture:
invite_link:
pattern: "https?://\\S+"
description: "One-time invite link — open in a browser to register your account"
sensitive: truebuild:- Top-level build block — this app uses a Dockerfile, not a prebuilt image.
commands:- The commands block defines start and bootstrap. No release or build command needed since the Dockerfile handles compilation.
bootstrap:- Runs on user request after the app is up. Creates an admin invite and captures the URL from stdout.
capture:- Extracts the invite link via regex — it's captured and surfaced to the user.
Check your understanding
- The deployment lifecycle is: build, release, start. Seed, test, and bootstrap are on-demand.
install/devrun the app from source (launchfile dev) — the source-mode pair ofbuild/start. Only these two are mode-aware.releaseruns once per deploy (migrations).bootstrapruns once ever (initial setup).- Commands can use the capture pattern to extract values from stdout via regex.
- Top-level
build:configures Docker image builds.commands.buildruns shell commands inside a container.