Development

Services

Define local development service tasks that lazy and datasets can coordinate.

By Guillermo Alvarez - Published Updated

Two kinds of services

Application services are Go packages under services/ and are wired into the app context. Read Dependencies And Services for that application code.

Development services are local processes such as PostgreSQL, MinIO, search, or queues. They are owned by mise tasks so every app can decide how those tools start and stop.

Name lifecycle tasks

Use one service name and a fixed set of actions:

mise postgres:start
mise postgres:check
mise postgres:create
mise postgres:migrate
mise postgres:dump -- datasets/baseline/postgres.dump
mise postgres:load -- datasets/baseline/postgres.dump

With file-based tasks, that usually means:

.mise/tasks/postgres/start
.mise/tasks/postgres/check
.mise/tasks/postgres/create
.mise/tasks/postgres/migrate
.mise/tasks/postgres/dump
.mise/tasks/postgres/load

postgres:start must run the service in the foreground and block until it is interrupted. During lazy development it runs as a managed subprocess, not an interactive shell.

postgres:check must exit with status 0 only when the service is up and ready for dependent commands. lazy runs it after starting the service process and before running create, migrate, or the app.

postgres:create creates the database, bucket, schema, or equivalent local resource if it is missing. It should be safe to run repeatedly.

postgres:migrate applies pending migrations. It should fail loudly if the database is reachable but migrations cannot run.

postgres:dump PATH writes service data to the provided path.

postgres:load PATH replaces or imports service data from the provided path. Document destructive behavior in the task itself.

No normal stop task is required when start runs in the foreground. Add a postgres:kill task only as an escape hatch for stale local processes.

List services

Declare services explicitly in lazy.toml when the app needs a stable order or when a service does not have a conventional :start task:

services = ["postgres", "minio"]

When lazy.toml lists services, lazy uses that list. Otherwise, lazy discovers services from task names that end in :start, such as .mise/tasks/postgres/start.

What lazy runs

When services are present, lazy starts each service in parallel after the development proxy and panel are already serving:

mise run postgres:start

Each service's stdout and stderr are recorded in the development panel and stay attached to the terminal. The panel's Services tab shows service output as a tree with the App service, managed services, lifecycle scripts, and general mise tasks below a separator. Output rows include source, run, stdout/stderr, timestamp, message, and parsed JSON attributes. Repeated readiness checks appear as separate runs. The bottom status bar shows each service with stopped, not-ready, or ready indicators. The Services tab can restart, stop, or start one managed service at a time; restart stops that service process and then runs the same start, check, create, and migrate flow for that service. If a check task exists, lazy calls it every 500ms. After five seconds of failures, lazy reports that the check is still failing and keeps checking until it succeeds.

After the service check succeeds, lazy runs the preparation tasks that exist, in this order:

postgres:check
postgres:create
postgres:migrate

Missing optional actions are skipped. create and migrate failures are reported and do not stop other services from finishing their startup work. The Go app starts after all services have finished startup preparation.

On Ctrl-C, lazy stops the app first, then stops all managed services. A second Ctrl-C escalates to killing child processes.

Read Lazy for the full development loop and Datasets for how dump and load are used.