Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test-impl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ jobs:
- run: mise run test:e2e e2e/cli/test_dotfiles_history_policies
- run: mise run test:e2e e2e/cli/test_dotfiles_rollback
- run: mise run test:e2e e2e/cli/test_dotfiles_rollback_types
- run: mise run test:e2e e2e/cli/test_bootstrap_user_services

lint:
runs-on: ${{ !inputs.trusted && 'ubuntu-latest' || 'namespace-profile-endev-linux-amd64-large;overrides.cache-tag=cache' }}
Expand Down
3 changes: 3 additions & 0 deletions docs/.vitepress/cli_commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ export const commands: { [key: string]: Command } = {
apply: {
hide: false,
},
remove: {
hide: false,
},
status: {
hide: false,
},
Expand Down
31 changes: 16 additions & 15 deletions docs/bootstrap/launchd.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,22 @@ as pipes and redirections need an explicitly invoked shell or a wrapper script.

## Supported keys

| TOML key | launchd key |
| ------------------------- | ------------------------- |
| `program` | `ProgramArguments[0]` |
| `args` | `ProgramArguments[1..]` |
| `run_at_load` | `RunAtLoad` |
| `keep_alive` | `KeepAlive` |
| `start_interval` | `StartInterval` |
| `throttle_interval` | `ThrottleInterval` |
| `start_calendar_interval` | `StartCalendarInterval` |
| `queue_directories` | `QueueDirectories` |
| `environment` | `EnvironmentVariables` |
| `working_directory` | `WorkingDirectory` |
| `stdout_path` | `StandardOutPath` |
| `stderr_path` | `StandardErrorPath` |
| `kickstart` | run `launchctl kickstart` |
| TOML key | launchd key |
| ------------------------- | ---------------------------------------- |
| `program` | `ProgramArguments[0]` |
| `args` | `ProgramArguments[1..]` |
| `run_at_load` | `RunAtLoad` |
| `keep_alive` | `KeepAlive` |
| `keep_alive_on_failure` | `KeepAlive = { SuccessfulExit = false }` |
| `start_interval` | `StartInterval` |
| `throttle_interval` | `ThrottleInterval` |
| `start_calendar_interval` | `StartCalendarInterval` |
| `queue_directories` | `QueueDirectories` |
| `environment` | `EnvironmentVariables` |
| `working_directory` | `WorkingDirectory` |
| `stdout_path` | `StandardOutPath` |
| `stderr_path` | `StandardErrorPath` |
| `kickstart` | run `launchctl kickstart` |

`program`, `working_directory`, `stdout_path`, `stderr_path`, and each entry in
`queue_directories` expand bare `~` and `~/` to the current user's home
Expand Down
131 changes: 122 additions & 9 deletions docs/bootstrap/services.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,121 @@
# System services
# Services

`[bootstrap.services]` declaratively manages the lifecycle of existing Linux
systemd system units. Package installation and `[bootstrap.files]` run first,
so a service may be installed by a package or supplied as a managed unit file.
After file changes, mise reloads systemd before applying service changes.
`[bootstrap.services]` declares services in two scopes:

- **System services** (the default) manage the lifecycle of existing Linux
systemd system units: start, stop, enable, mask, and reload on change.
- **User services** (`scope = "user"`) are services mise defines for the
current user, declared once and installed on every platform: a systemd user
unit on Linux, a LaunchAgent on macOS, a Scheduled Task on Windows.

## User services

```toml
[bootstrap.services.mise-history] # the built-in history watcher
builtin = "history-watch" # implies scope = "user"

[bootstrap.services.my-agent]
scope = "user"
command = "~/.local/bin/my-agent --serve"
description = "My agent"
restart = "on-failure" # "always" | "on-failure" | "never"
environment = { RUST_LOG = "info" }
working_directory = "~"
requires_tools = true # converge after [tools] are installed
```

One declaration is rendered for the platform's user service manager:

| platform | definition | manager |
| -------- | ------------------------------------------------------------------------------------- | ------------------ |
| Linux | `~/.config/systemd/user/dev.mise.<name>.service` | `systemctl --user` |
| macOS | `~/Library/LaunchAgents/dev.mise.<name>.plist` | `launchctl` |
| Windows | Scheduled Task `mise\<name>` (definition kept under `$MISE_STATE_DIR/user-services/`) | `schtasks` |

### User service options

- `command`: the command line to run. `~` and `~/` are expanded. Required
unless `builtin` is set.
- `builtin`: a definition mise supplies. `"history-watch"` runs
`mise bootstrap dotfiles watch` through a durable mise executable with
`restart = "on-failure"` and a low priority. A builtin implies
`scope = "user"`; `command` cannot be combined with it.
- `description`: shown by the service manager.
- `restart`: `"on-failure"` (default), `"always"`, or `"never"`. On Linux this
is `Restart=`; on macOS `KeepAlive` (`{ SuccessfulExit = false }` for
on-failure). Task Scheduler restarts only failed runs, so on Windows
`"always"` and `"on-failure"` both restart up to three times a minute apart
after a failure and run again at logon (when `enabled = true`); a clean
exit is not restarted. Strict `"always"` semantics are a Linux and macOS
feature; a service that must survive a clean exit on Windows should loop
inside its own program.
- `environment` and `working_directory` map directly to the platform
definition. On Windows, environment variables are set through `cmd.exe`,
so values containing characters it would reinterpret (`%`, `"`, `&`, `|`,
`<`, `>`, `^`) are rejected, and so is a `command` containing `%`, `&`,
`|`, `<`, `>`, or `^` once `environment` is set (without `environment`
the command runs directly). Move such a command into a script, or set the
variables inside the program.
- `state`: `"running"` (default), `"stopped"` (installed but not running), or
`"absent"` (the installed definition is removed and stays removed while
declared so).
- `enabled`: whether the service starts at login (default `true`). On macOS
this is `RunAtLoad`, which launchd also honours when the agent is loaded,
so a stopped agent is written without it (it starts at login again once it
is set running). launchd reads any `KeepAlive` as run-at-load too, so an
agent with `enabled = false` is written without one: it is started once by
the apply but neither starts at login nor is restarted after a failure
until it is enabled again.
- `requires_tools`: converge in a second pass after `[tools]` and plugin
package managers, so a service that runs a tool starts after it exists. The
built-in watcher needs only mise and converges in the services step.

Names must contain only letters, numbers, `.`, `_`, or `-`, and must not also
appear in `[bootstrap.linux.systemd.units]` or
`[bootstrap.macos.launchd.agents]`: both would write the same definition.

### Durable executable

A builtin is written with an absolute path to the mise that installed it.
mise uses the running executable unless it lives in a temporary directory or
in the staging directory of `mise bootstrap remote`, and otherwise a `mise`
found on `PATH` outside those. When only a staged binary exists the service is
reported as `unknown: no durable mise executable; install mise on this host
first` and is never written with a path that will be deleted.

### Remove and disable

`state = "absent"` removes the installed unit, agent, or task and keeps it
absent on later runs while declared so. Deleting the declaration leaves the
installed service in place until it is removed once:

```sh
mise bootstrap services remove my-agent
```

The next `mise bootstrap` recreates it if it is still declared.

### Status and apply

`mise bootstrap services status` and `mise bootstrap services apply` cover
both scopes; `mise bootstrap status` and `mise bootstrap plan` list user
services as `user-service:<name>`. `mise bootstrap status --json` includes
each user service's rendered definition under `user_services`, so what mise
would install can be inspected before applying. When the platform's user service manager is unavailable (for
example, no systemd user manager in a container), user services are reported
as `unknown` and skipped with a follow-up note; nothing is written.

Fields that only apply to user services (`command`, `builtin`, `description`,
`restart`, `environment`, `working_directory`, `requires_tools`, and
`state = "absent"`) are rejected on a system-scope entry, so a missing
`scope = "user"` cannot silently turn a service definition into a lookup of a
system unit. Managed-file notifications apply to system services only.

## System services

Package installation and `[bootstrap.files]` run first, so a service may be
installed by a package or supplied as a managed unit file. After file changes,
mise reloads systemd before applying service changes.

```toml
[bootstrap.packages]
Expand All @@ -17,15 +129,16 @@ enabled = true
Names without a unit suffix receive `.service`. Explicit unit names such as
`postgresql@16-main.service`, sockets, and timers are also accepted.

For user-owned units written under `~/.config/systemd/user`, use
[systemd user units](/bootstrap/systemd.html) instead. This section manages
system units already supplied by packages or [managed files](/bootstrap/files.html).
This section manages system units already supplied by packages or
[managed files](/bootstrap/files.html). A service that runs as your user is a
[user service](#user-services) (`scope = "user"`, above); hand-written user
units go through [systemd user units](/bootstrap/systemd.html).

Preview with `mise bootstrap services apply --dry-run`. If the unit will be
created by the same configuration, use the full bootstrap to install its package
or file before converging the service.

## Options
### System service options

- `state`: `"running"` (default) or `"stopped"`
- `enabled`: whether the unit starts at boot (default `true`)
Expand Down
5 changes: 3 additions & 2 deletions docs/cli/bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ Runs the bootstrap steps for the current config in order:
2. Install built-in-manager entries from `[bootstrap.packages]`
3. `mise bootstrap files apply` — converge `[bootstrap.files]` and
`[bootstrap.directories]`
4. `mise bootstrap services apply` — converge `[bootstrap.services]`
systemd system services (Linux)
4. `mise bootstrap services apply` — converge `[bootstrap.services]`:
systemd system services (Linux) and user-scope services on every
platform (those with `requires_tools = true` converge after step 14)
5. `mise bootstrap firewall apply` — converge `[bootstrap.linux.firewall]`
host firewall policy and rules (Linux)
6. `mise bootstrap compose apply` — converge `[bootstrap.compose]`
Expand Down
8 changes: 7 additions & 1 deletion docs/cli/bootstrap/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
- **Effect:** read-only
- **Source code:** [`src/cli/bootstrap.rs`](https://github.com/jdx/mise/blob/main/src/cli/bootstrap.rs)

Manage Linux system services from `[bootstrap.services]`
Manage services from `[bootstrap.services]`

System-scope entries (the default) converge existing Linux systemd system
units. `scope = "user"` entries are services mise defines for the current
user on every platform: a systemd user unit on Linux, a LaunchAgent on
macOS, a Scheduled Task on Windows.

## Flags
- **`-h --help`** — Print help

## Subcommands

- [`mise bootstrap services apply [-n --dry-run] [-y --yes]`](/cli/bootstrap/services/apply.md)
- [`mise bootstrap services remove [-n --dry-run] <NAME>`](/cli/bootstrap/services/remove.md)
- [`mise bootstrap services status [-J --json] [--missing]`](/cli/bootstrap/services/status.md)
2 changes: 1 addition & 1 deletion docs/cli/bootstrap/services/apply.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- **Effect:** destructive — may delete or irreversibly overwrite
- **Source code:** [`src/cli/bootstrap.rs`](https://github.com/jdx/mise/blob/main/src/cli/bootstrap.rs)

Apply configured Linux system service state
Apply configured service state (system and user scope)

## Flags
- **`-n --dry-run`** — Print what would change without changing anything
Expand Down
19 changes: 19 additions & 0 deletions docs/cli/bootstrap/services/remove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!-- @generated by usage-cli from usage spec -->
# `mise bootstrap services remove`

- **Usage:** `mise bootstrap services remove [-n --dry-run] <NAME>`
- **Effect:** destructive — may delete or irreversibly overwrite
- **Source code:** [`src/cli/bootstrap.rs`](https://github.com/jdx/mise/blob/main/src/cli/bootstrap.rs)

Remove an installed user-scope service, declared or not

Deleting a `scope = "user"` declaration leaves its installed unit, agent,
or task in place; this removes it once. The next `mise bootstrap`
recreates it if it is still declared.

## Arguments
- **`<NAME>`** — The installed user-service name to remove (declared or not)

## Flags
- **`-n --dry-run`** — Print what would change without changing anything
- **`-h --help`** — Print help
2 changes: 1 addition & 1 deletion docs/cli/bootstrap/services/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- **Effect:** read-only
- **Source code:** [`src/cli/bootstrap.rs`](https://github.com/jdx/mise/blob/main/src/cli/bootstrap.rs)

Show configured Linux system service state
Show configured service state (system and user scope)

## Flags
- **`-J --json`** — Output in JSON format
Expand Down
1 change: 1 addition & 0 deletions docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
- [`mise bootstrap secrets status [-J --json] [--missing]`](/cli/bootstrap/secrets/status.md)
- [`mise bootstrap services <SUBCOMMAND>`](/cli/bootstrap/services.md)
- [`mise bootstrap services apply [-n --dry-run] [-y --yes]`](/cli/bootstrap/services/apply.md)
- [`mise bootstrap services remove [-n --dry-run] <NAME>`](/cli/bootstrap/services/remove.md)
- [`mise bootstrap services status [-J --json] [--missing]`](/cli/bootstrap/services/status.md)
- [`mise bootstrap status [FLAGS]`](/cli/bootstrap/status.md)
- [`mise bootstrap systemd apply [-n --dry-run] [-y --yes]`](/cli/bootstrap/systemd/apply.md)
Expand Down
4 changes: 2 additions & 2 deletions docs/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
- [Package Plugins](https://mise.jdx.dev/bootstrap/packages/plugins.html): Package manager plugins extend [bootstrap.packages] without adding a manager to mise core. They are useful for machine-global state owned by another tool, such as VS Code extensions, Helm plugins,…
- [Linux Users and Groups](https://mise.jdx.dev/bootstrap/accounts.html): [bootstrap.groups] and [bootstrap.users] declaratively manage local Linux accounts. mise applies groups before users and applies accounts before privileged files, so a managed file can safely refer to…
- [System Files](https://mise.jdx.dev/bootstrap/files.html): [bootstrap.files] and [bootstrap.directories] declaratively manage absolute paths that may require root privileges. They are separate from [dotfiles], which manages files in a user's home directory.
- [System Services](https://mise.jdx.dev/bootstrap/services.html): [bootstrap.services] declaratively manages the lifecycle of existing Linux systemd system units. Package installation and [bootstrap.files] run first, so a service may be installed by a package or…
- [System Services](https://mise.jdx.dev/bootstrap/services.html): [bootstrap.services] declares services in two scopes.
- [Docker Compose Projects](https://mise.jdx.dev/bootstrap/compose.html): [bootstrap.compose] declaratively manages long-running Docker Compose projects after packages, privileged files, directories, and system services have converged.
- [Secret Inputs](https://mise.jdx.dev/bootstrap/secrets.html): [bootstrap.secrets] declares the sensitive inputs a bootstrap configuration needs without storing their values in mise configuration.
- [Repos](https://mise.jdx.dev/bootstrap/repos.html): mise can declare git repositories in [bootstrap.repos] and apply them with mise bootstrap repos apply or as part of mise bootstrap.
Expand Down Expand Up @@ -186,7 +186,7 @@
- [mise bootstrap remote](https://mise.jdx.dev/cli/bootstrap/remote.html): Bootstrap one or more machines over OpenSSH
- [mise bootstrap repos](https://mise.jdx.dev/cli/bootstrap/repos.html): Manage git repo checkouts from [bootstrap.repos]
- [mise bootstrap secrets](https://mise.jdx.dev/cli/bootstrap/secrets.html): Inspect bootstrap secret inputs without revealing their values
- [mise bootstrap services](https://mise.jdx.dev/cli/bootstrap/services.html): Manage Linux system services from [bootstrap.services]
- [mise bootstrap services](https://mise.jdx.dev/cli/bootstrap/services.html): Manage services from [bootstrap.services]
- [mise bootstrap status](https://mise.jdx.dev/cli/bootstrap/status.html): Show the aggregate bootstrap status
- [mise bootstrap user](https://mise.jdx.dev/cli/bootstrap/user.html): Manage current-user bootstrap settings from [bootstrap.user]
- [mise cache](https://mise.jdx.dev/cli/cache.html): Manage the mise cache
Expand Down
Loading