DocsBlinkRuntimes

Runtimes

The shell, go, node, and docker runtimes and the build, run, and watch defaults each contributes.

Every service has a runtime that names its lifecycle owner. Empty means shell. The other three read a typed block of the same name and synthesize commands, watch defaults, and managers. Whatever a runtime contributes is a default, so anything you set explicitly wins. Each runtime resolves the same defaults blink init writes, so a minimal hand-written block behaves like a generated one.

shell (default)

The fallback. When runtime is empty or shell, blink runs whatever you put under commands: as sh -c. The shell runtime contributes no overlay, no watch defaults, and no manager.

  - name: worker
    runtime: shell
    commands:
      run:
        command: ./worker
        service: true      # a long-running process, not a one-shot
    reload:
      reload: true
    fs:
      extensions: [go]

Warning

Reload is not implied by a runtime. A shell service that should restart on edits needs an explicit reload: true and an fs block. The go, node, and docker runtimes set this up for you.

go

Synthesizes a build plus binary-run pair, and when the repo uses a go.work workspace, adds every workspace module as an extra watch root so a cross-module edit rebuilds.

  - name: api
    runtime: go
    go:
      package: ./cmd/api      # required
      args: ["--port", "8080"]
      out: ./bin/api          # optional, defaults under .blink/build
      workspace: true         # optional, auto-detected from go.work

Build is go build -o <out> <package>, run is <out> <args...> marked as a long-running service. The default watched extensions are go, mod, sum. With a go.work present, blink parses its use directives and adds each module (except the service's own dir) as a recursive watch root. Set workspace: false to opt out even when a go.work exists.

node

Detects the package manager from the lockfile and synthesizes <pm> run <script>, optionally preceded by a guarded install.

  - name: web
    runtime: node
    node:
      script: dev             # optional, defaults to dev then start
      package_manager: pnpm   # optional, auto-detected from lockfile
      install: true           # optional, defaults to true

When script is unset the runtime reads package.json and picks dev if present, else start. Package-manager detection order is pnpm, then bun, then yarn, then npm, keyed on the first lockfile present, falling back to npm. Install is a setup step, so it runs once on boot and again only when package.json changes, never on a source reload.

A self-reloading dev server (vite, next, nuxt, astro, remix, sveltekit, parcel, nodemon, tsx watch, --watch, and similar) is detected from the resolved command. blink then assumes the tool handles hot reload itself and scopes the watch to package.json only, so source edits never restart the process. A plain node server keeps a broad js/jsx/ts/tsx/mjs/cjs/json/css/html watch.

docker

Drives docker compose, watches container state via docker events, and by default streams every container's logs multiplexed into the UI. File-change reload is off, because compose owns the containers.

  - name: infra
    runtime: docker
    docker:
      file: docker-compose.yml   # optional, probed when empty
      project: myapp             # optional, defaults to basename of dir
      services: [db, redis]      # optional, empty = all in compose file
      logs: [db]                 # optional, empty = stream all containers
      wait: true                 # optional, defaults true
      stop_on_exit: false        # optional, defaults false
      log_tail: 500              # optional, defaults 500, <=0 = all

services and logs are distinct axes. services controls which compose services are started, and logs controls which running containers blink follows in the UI. stop_on_exit defaults false so containers persist between runs and the next boot reuses warm databases, and pre-existing containers blink did not start are never touched.

When file is unset the runtime probes compose.yaml, compose.yml, docker-compose.yaml, docker-compose.yml in that order and falls back to docker-compose.yml when none exist.

Full field tables for every runtime block live in the runtime reference.