◢ この記事は以下の言語でも利用できます: English
mise bootstrap は、マシン 1 台のセットアップ手順を設定ファイルに宣言しておき、コマンド 1 回でその状態に揃える機能です。OS のパッケージ、dotfiles、シェルの有効化、開発ツール、最後に流す仕上げのタスクまでを、1 つの config.toml が受け持ちます。
mise 入門ガイドでは、ツールのバージョンを「打った人ではなくディレクトリに結びつける」と書きました。bootstrap はその発想をマシン全体に広げたものです。プロジェクトに mise.toml があれば mise install で揃うように、マシンに設定があれば mise bootstrap で揃います。
新しいマシンを用意するたびに、同じことをしていました。
history から拾い直すmise activate を足すmise install手順はどこかにメモしてあります。ただ、そのメモは前回のセットアップ以降に足したものを知りません。抜けに気付くのは、たいてい何かが動かなくなってからです。
Ansible や Nix で解決できる問題ですが、個人の作業マシン 2〜3 台のために持ち込むには大きすぎると感じていました。すでに mise を使っているなら、そこに書けるのが一番軽いです。
書く場所は、プロジェクトの mise.toml ではなくユーザーの設定ファイルです。マシンのセットアップはプロジェクトではなくその人のものなので、~/.config/mise/config.toml に置きます。
[bootstrap.packages]
"winget:BurntSushi.ripgrep.MSVC" = { os = "windows" }
"winget:Git.Git" = { os = "windows" }
[tools]
node = "22"
[tasks.bootstrap]
run = "node --version"3 つの節があります。
[bootstrap.packages] — OS のパッケージマネージャで入れるもの。マネージャ名:パッケージ名 の形で書き、os で対象を絞れます[tools] — いつもの mise のツール。ここは何も変わりません[tasks.bootstrap] — 全部が終わったあとに走るタスク。動作確認や、宣言では書けない仕上げに使います[env] を含む設定と同じで、初回は mise trust で承認します。
いきなり走らせる必要はありません。3 つの見方があります。
--dry-run実際に流れる順番で、実行せずに表示します。
❯ mise bootstrap --dry-run
mise bootstrap: system packages
mise winget: 1 package(s) already installed
winget install --id BurntSushi.ripgrep.MSVC --exact --silent --accept-source-agreements --accept-package-agreements --disable-interactivity
mise bootstrap: tools
mise all tools are installed
mise bootstrap: running `bootstrap` task
[bootstrap] $ node --versionGit は入っていると判定され、ripgrep だけが winget install の対象になっています。実際に打たれるコマンドがそのまま出るので、--silent や --accept-* のようなフラグの扱いも確認できます。
plan宣言したリソースが、今の状態からどう変わるかを表にします。
❯ mise bootstrap plan
create package:winget:BurntSushi.ripgrep.MSVC missing installed (any version)
unchanged package:winget:Git.Git installed (2.55.0.3) installed (any version)
Plan: 1 create, 0 update, 1 unchanged, 0 remove, 0 unknownOpenTofu や Terraform の plan と同じ読み方です。左から「操作」「リソース」「現在」「望む状態」で、最後に集計が出ます。
statusいま何がどうなっているかの一覧です。
❯ mise bootstrap status
packages winget:BurntSushi.ripgrep.MSVC missing
packages winget:Git.Git 2.55.0.3 installed
tools node@22 22.23.2 installedmise bootstrap手元の ripgrep は winget 以外で入れてあったので、この記事ではパッケージの節を飛ばして流しました。飛ばし方は次の節で説明します。
❯ mise bootstrap --skip packages
mise bootstrap: dotfiles
mise files: all files are applied
mise bootstrap: tools
mise ⇢ node@22 71ms · already installed
mise all tools are installed
mise bootstrap: running `bootstrap` task
[bootstrap] $ node --version
v22.23.2同じコマンドをもう一度打つと、揃っているものは流れていくだけです。変わっていないリソースは飛ばされます。
実行のたびに、前後の状態がチェックポイントとして記録されます。
❯ mise bootstrap dotfiles history
12 2026-09-12 16:52 bootstrap bootstrap
11 2026-09-12 16:52 bootstrap-before before bootstrap
10 2026-09-12 16:52 bootstrap bootstrap
9 2026-09-12 16:52 bootstrap-before before bootstrap
8 2026-09-12 16:51 bootstrap (failed) bootstrap
7 2026-09-12 16:51 bootstrap-before before bootstrap失敗した回も (failed) として残ります。この一覧は dotfiles の節で戻ってきます。
mise bootstrap は、宣言した節を決まった順で処理します。ざっくり言うと、土台から順に、依存されるものが先です。
[bootstrap.packages])とその前後のファイル[tools] のツール[tasks.bootstrap]、最後の hookパッケージが dotfiles より先で、[tasks.bootstrap] がいちばん後ろです。dotfiles が前提にするエディタやシェルは先に入っていて、仕上げのタスクを書く時点では宣言したものが全部使える、という順番だと読めます。
一部だけ流したいときは --only と --skip があります。どちらも繰り返し指定でき、同時には使えません。
mise bootstrap --skip packages # パッケージ以外を流す
mise bootstrap --only tools # ツールだけ指定できる名前は packages dotfiles shell tools task など、上の節の名前です。間違えるとエラーが候補を全部教えてくれます。
マネージャ名:パッケージ名 を書きます。マネージャ名は省略できません。同じ ripgrep でも、apt と brew と winget では名前が違うからです。
[bootstrap.packages]
"apt:ripgrep" = { os = "linux" }
"brew:ripgrep" = { os = "macos" }
"winget:BurntSushi.ripgrep.MSVC" = { os = "windows" }
"brew-cask:font-jetbrains-mono" = { os = ["linux", "macos"] }
"pacman:libreoffice-fresh" = { state = "absent" }os で絞れば、1 つの設定ファイルを OS の違う複数のマシンで共有できます。state = "absent" は「入っていたら消す」です。
対応しているマネージャは、apt / apk / dnf / pacman / aur / brew / brew-cask / flatpak / nix / mas / winget です。Windows の winget は PATH にあれば使われます。
1 つ、挙動として知っておきたいのは "latest" は「入っていればよい」という意味だということです。すでに入っているパッケージを、実行のたびに最新へ上げることはしません。上げたいときは mise bootstrap packages upgrade を明示的に打ちます。マシンの状態を揃えるコマンドが、勝手に更新を始めないのは正しい設計だと思います。
一番おもしろいのがここです。dotfiles をリンクするだけでなく、変更の履歴を持ちます。
まず、今あるファイルを追跡対象にします。
❯ mise bootstrap dotfiles track ~/.bashrc
mise history: saved baseline checkpoint 1
mise dotfiles: tracking ~/.bashrc (declared in ~/.config/mise/config.toml)
mise WARN history: automatic capture is inactive: declare `[bootstrap.services.mise-history] builtin = "history-watch"` and run `mise bootstrap`; until then edits are saved by `mise bootstrap dotfiles save` or `mise bootstrap dotfiles watch --once`ファイルはそのままで、今の中身がチェックポイント 1 として保存され、設定ファイルにこう足されます。
[dotfiles]
"~/.bashrc" = { mode = "track" }ファイルを編集して、保存します。
❯ echo 'alias gs="git status"' >> ~/.bashrc
❯ mise bootstrap dotfiles save ~/.bashrc
mise history: saved checkpoint 2: edited ~/.bashrc履歴と差分が見られます。
❯ mise bootstrap dotfiles history --path ~/.bashrc
2 2026-09-12 16:50 save edited ~/.bashrc
1 2026-09-12 16:50 baseline tracked ~/.bashrc❯ mise bootstrap dotfiles history diff 2 --path ~/.bashrc --patch@@ -1,2 +1,3 @@
export EDITOR=vim
alias ll="ls -la"
+alias gs="git status"そして戻せます。
❯ mise bootstrap dotfiles rollback ~/.bashrc
mise history: rolled back ~/.bashrc to checkpoint 1rollback は「今のファイルと中身が違う、いちばん新しい保存」を選びます。設定ファイルをいじって壊したとき、git stash の代わりに使える感覚です。
track したときの出力に automatic capture is inactive という WARN が出ていました。既定では、編集のたびに save を打たないとチェックポイントが取られない、という意味です。同じ WARN が案内しているとおり、変更を自動で拾うサービスを宣言すれば、この手間は無くなります。
[bootstrap.services.mise-history]
builtin = "history-watch"Linux では systemd の user service、macOS では LaunchAgent、Windows ではタスクスケジューラのタスクとして登録されます。
追跡ではなく、リポジトリにある dotfiles を配置する側の書き方です。
[settings]
dotfiles.root = "~/.dotfiles"
[dotfiles]
"~/.config/nvim" = { source = "nvim", mode = "symlink" }
"~/.gitconfig" = { source = "gitconfig.tera", mode = "template" }
"~/.zshrc/activate" = { block = 'eval "$(mise activate zsh)"' }symlink(既定)— リンクを張る。配置先で編集すればリポジトリ側が変わるcopy — コピーする。配置先を直接編集しても、次の実行で上書きされるtemplate — テンプレートを描画して書き出す。{{ vars.email }} のように、マシンごとの値を埋め込めるblock / line — 既存のファイルの中に、マーカーで囲んだ一部分だけを管理するsource を省くと dotfiles.root の下から、配置先と同じ相対パスで探します。~/.zshrc なら ~/.dotfiles/.zshrc です。
Windows では、シンボリックリンクに開発者モードが必要です。無ければ mise はコピーに切り替えます。ディレクトリのリンクはジャンクションになります。
mise activate の 1 行を、シェルの設定ファイルに書く作業も宣言にできます。
[bootstrap.mise_shell_activate]
zprofile = "shims"
zshrc = "activate"
bashrc = "activate"
fish = "activate"書き込まれるのはマーカーで囲まれたブロックだけです。
# >>> mise:activate >>>
eval "$(mise activate zsh)"
# <<< mise:activate <<<mise はこの範囲だけを書き換えるので、他の行には触りません。
ここまでの設定を Git のリポジトリに置いておけば、次のマシンは 2 行で揃います。ここからは、まっさらな Debian のコンテナを 2 台目に見立てて試しました。
curl https://mise.run | sh
mise bootstrap --adopt you/setup --yes--adopt が何をするかは --dry-run が 1 行で教えてくれます。
Would run: git clone /setup ~/.config/miseリポジトリそのものが ~/.config/mise になります。 なので、リポジトリの中身は設定ディレクトリの形にしておきます。
setup/
config.toml
dotfiles/
gitconfig[bootstrap.packages]
"apt:ripgrep" = { os = "linux" }
[bootstrap.mise_shell_activate]
bashrc = "activate"
[dotfiles]
"~/.gitconfig" = { source = "dotfiles/gitconfig", mode = "copy" }
[tools]
node = "22"
[tasks.bootstrap]
run = ["rg --version | head -1", "node --version"]実行すると、上から順に流れます。
Cloning into '/root/.config/mise'...
mise bootstrap: system packages
mise $ apt-get install -y -- ripgrep
mise apt: installed ripgrep
mise bootstrap: dotfiles
mise files: copied ~/.config/mise/dotfiles/gitconfig to ~/.gitconfig
mise bootstrap: shell activation
mise edits: applied ~/.bashrc (block:activate)
mise bootstrap: tools
mise ✓ node@22.23.2 48.7s node-v22.23.2-linux-x64.tar.gz
mise bootstrap: running `bootstrap` task
[bootstrap] $ rg --version | head -1
ripgrep 14.1.1
[bootstrap] $ node --version
v22.23.2SSH で届く先なら、向こうで何も打たずに済みます。
mise bootstrap remote --host devbox --adopt ~/src/setup --install-mise --yes向こうに mise が入っている必要はありません。 手元の mise を転送して、向こうの /tmp で展開して動かします。--adopt にローカルのパスを渡すと、リポジトリは git bundle にして一緒に送られるので、向こうからリポジトリに届く必要もありません。
mise bootstrap remote root@172.17.0.4 (root@172.17.0.4)
2026.9.5 linux-x64 (2026-09-10)
mise bootstrap: system packages
mise apt: installed ripgrep
...
mise remote bootstrap completed on 1 target(s)フラグを 2 つ付けているのには、それぞれ踏んだ理由があります。
--install-mise — 付けないと、転送した mise は終了後に消えます。ところが [bootstrap.mise_shell_activate] が .bashrc に書いた eval "$(mise activate bash)" は残るので、次にシェルを開いた瞬間から mise: command not found が出続けます。付ければ ~/.local/bin/mise に残ります(~/.local/bin を PATH に通す作業は、curl | sh で入れたときと同じく自分でやります)--adopt — --source というフラグもあり、こちらはディレクトリを「プロジェクト」として送ります。mise.toml を置いた開発用のディレクトリを向こうに再現する用途で、[tools] はそのプロジェクトの中でしか効きません。試すと、node は入ったのに No version is set for shim: node になりました。マシンの設定を流したいなら --adopt ですmise bootstrap は、マシンのセットアップを ~/.config/mise/config.toml に宣言して 1 回で揃える。プロジェクトの mise.toml と同じ発想をマシンに広げたもの--dry-run / plan / status で何が起きるかを見られる。plan は Terraform の plan と同じ読み方[tasks.bootstrap] だけは毎回走るので、繰り返せる内容にする"latest" は「入っていればよい」。更新は packages upgrade で明示的にtrack するだけでチェックポイントが取れ、history と rollback で戻せる--adopt でリポジトリを ~/.config/mise として clone する。SSH 先には remote --adopt --install-mise で、向こうに mise が無くても流せるマシンのセットアップは、やり直す機会が少ないぶん手順が風化しやすい作業です。設定ファイルに書いて --dry-run で読めるようにしておけば、少なくとも「前回何をしたか」を思い出す作業は無くなります。
Meta 製の Git 互換 VCS「Sapling (sl)」を、インストールからスタック、sl pr submit での PR 作成、ReviewStack、GUI の sl web まで
Git 互換のバージョン管理システム Jujutsu (jj) を、インストールから Git との違い、毎日打つコマンドまで
ディレクトリに入ったときだけ効く環境変数を mise.toml に書く方法と、.env を読み込むときの落とし穴