Skip to main content

> Jitrak Blog

>_chezmoi for Dotfiles Across Machines

#chezmoi for Dotfiles: What and Why — ทำไมใช้ chezmoi จัดการ Dotfiles, git init ที่ $HOME ไม่พอ และเทียบกับ Bare Git, GNU Stow, dotbot, Ansible, Nix home-manager

@Yosapol Jitrak|2 Nov 2026 03:37…

จากบทความที่แล้ว My Agent Harness: The System Around the Model ผมเล่าเรื่อง Rule, Skill, Hook และ MCP ที่ห่อ AI Model อยู่ ซึ่ง Setup ค่อนข้างเยอะ ปกติอยู่บ้านผมทำงานบน WSL แต่บางครั้งต้องออกไปทำงานนอกบ้านด้วย MacBook ปัญหาที่เจอคือ AI ทำงานไม่ได้ดั่งใจ เพราะ Harness ติดตั้งไว้ไม่ครบ บางตัวมี บางตัวไม่มี นอกจากนี้ผมยังมี PC อีกเครื่องที่ลง Hermes ไว้ใช้งาน ก็เจอปัญหาเดียวกัน

Harness ทั้งชุดอยู่ใน Home Directory

ของใน Harness แทบไม่มีตัวไหนอยู่ใน Repo ของ Project เลย มันอยู่ใน ~/.cursor กับ ~/.agents จริง ๆ จะใส่ไว้ใน Project ก็ได้ แต่ต้องมานั่งใส่ทุก Repo ก็ไม่ไหว

เรื่องติดตั้ง Application ผมเคยเล่าวิธีแก้ไปแล้ว Mac ใช้ Homebrew, Cask และ mas-cli ฝั่ง Windows ใช้ Winget

แต่ Package Manager ไม่ได้เอา Config กลับมาให้ด้วย Agent บนเครื่องใหม่จึงเริ่มจากศูนย์ ไม่รู้ Rule ไม่มี Skill ไม่มี Hook แถมยังขาด .zshrc, .gitconfig, .vimrc และอื่น ๆ

แต่ละตัวเก็บสิ่งที่เคยตั้งไว้คนละเรื่อง ยกตัวอย่างเช่น

  • .zshrc - PATH, Homebrew, pnpm และโหลด Alias ของ Shell
  • .eji4h_bash_aliases/ - Custom Alias ของผมดั่งเดิม แยกตาม Tool เช่น Git, kubectl, gcloud, Terragrunt
  • .gitconfig - Identity ของ Git แยก .gitconfig-personal กับ .gitconfig-work ตาม Folder งาน ตามที่เล่าไว้ใน Manage multiple Git account in machine
  • .vimrc - Config ของ Vim
  • .tmux.conf - Config ของ tmux
  • .cursor/ และ .agents/ - Rule, Skill และ Hook ของ AI Agent

บางไฟล์ติดมากับการ Setup บางไฟล์ผมเขียนเองและแก้เพิ่มอยู่เรื่อย ๆ ของแบบนี้ควรอยู่ใน Version Control เหมือน Source Code

Dotfiles คืออะไร

Dotfiles คือไฟล์ Config ที่ชื่อขึ้นต้นด้วยจุด อย่าง .zshrc หรือ .gitconfig ซึ่งอยู่ใน Home Directory ($HOME) จุดข้างหน้าเป็นธรรมเนียมของ Unix ว่าไฟล์นี้เป็นของโปรแกรม ไม่ใช่งานของเรา ls กับ File Manager เลยซ่อนไว้เป็นค่าเริ่มต้น

$ ls ~
Downloads Library works

$ ls -a ~              # a = all รวมไฟล์ที่ซ่อนอยู่
.agents  .config  .cursor  .gitconfig  .p10k.zsh  .ssh  .zshrc Downloads Library works

ทำไม git init ที่ $HOME ไม่พอ

ทางแรกที่ผมนึกออกคือ git init ที่ $HOME แล้ว git add เฉพาะไฟล์ที่ต้องการ ทางนี้ติด 2 เรื่อง

ไฟล์กระจาย — $HOME มีทั้งโฟลเดอร์งาน, Downloads และ Cache ของโปรแกรม git status จะขึ้น Untracked File เป็นพัน และ git add . พลาดครั้งเดียว ของที่ไม่ควรเข้าก็เข้า Repo

ต่างเครื่องต่าง Config — macOS เก็บ Home ที่ /Users/${USERNAME} ส่วน WSL อยู่ที่ /home/${USERNAME}

เทียบเครื่องมือ

Comparison Table ฉบับเต็มของ chezmoi เทียบกับ dotbot, rcm, vcsh, yadm และ Bare Git Credit: chezmoi Comparison Table (Capture เมื่อ 2 พฤศจิกายน 2026)

รูปด้านบนอยู่ใน Website ของ chezmoi แต่จริง ๆ มันก็ยังมีตัวเลือกอื่นอีก ในรูปจะมี Link https://dotfiles.github.io/utilities/ ที่ดัง ๆ เลยก็จะมี GNU Stow กับ Nix home-manager และสำหรับคนทำ Infra อาจจะนึกถึง Ansible อีกตัวด้วย

ไล่ตัดทีละตัว

โจทย์ของผมคือ 3 เครื่อง 2 ตระกูล OS — WSL สองเครื่อง (eji4h-pc, eji4h-server) กับ MacBook Apple Silicon (eji4h-macbook)

Dotfiles sync ผ่าน Git Repo ไป 3 เครื่อง

Bare Git เบาที่สุด มี Git ก็พอ

git init --bare $HOME/.dotfiles
alias dot='git --git-dir=$HOME/.dotfiles --work-tree=$HOME'
dot config status.showUntrackedFiles no

แต่ไม่มีกลไกในตัวสำหรับไฟล์ที่ต่างกันต่อเครื่อง ต้องแยก Branch หรือแยกไฟล์เอง แล้วรวมส่วนที่ใช้ร่วมกันด้วยมือ

GNU Stow มี Mental Model ง่ายที่สุด แยกโฟลเดอร์ละ Package แล้ว Stow สร้าง Symlink กลับไปที่ $HOME ให้ แต่ก็ไม่มี Template เหมือน Bare Git

dotbot คือ Stow ที่มี Install Script ในตัว YAML ไฟล์เดียวบอกทั้ง Link และคำสั่งที่ต้องรัน ใกล้สิ่งที่ผมต้องการมาก แต่ก็ไม่มี Template ต่างเครื่องยังต้องแยกไฟล์เอง

Ansible และ Nix home-manager ผ่านเกณฑ์เกือบหมด แต่มาพร้อม Ecosystem ทั้งชุด Ansible คือ Playbook กับ Python ที่ออกแบบมาดูแล Server เป็นฝูง ส่วน Nix คือทั้งภาษาและ Package Manager ใหม่ทั้งชุด ผมแค่อยาก Sync Config ไม่ได้อยากทำให้มันซับซ้อนมาก

เกณฑ์Bare GitGNU StowdotbotchezmoiAnsibleNix home-manager
ต้องมีก่อน BootstrapgitPerlPython¹ไม่มี²PythonNix³
Dotfiles เป็นFileSymlinkSymlinkFileแล้วแต่ ModuleSymlink ไป Nix Store
ดู Diff ก่อน Apply✅ git diff⚠️ stow -n⁴⚠️ --dry-run⁴✅ chezmoi diff✅ --check --diff⚠️ ไม่มี Diff ของ $HOME⁴
ดึง Secret จาก Password Manager❌❌❌✅⚠️ Lookup Plugin❌⁵
ต่างเครื่องต่าง Config⚠️ ไม่มีในตัว⁶แยก Packageแยกไฟล์TemplateTemplate + Host VarsNix Module
Run Scripts❌❌✅✅✅✅

¹ Runtime ของ dotbot คือ Python. git ไม่ใช่ Dependency ของตัวโปรแกรม แต่ Script ./install ค่าเริ่มต้นเรียก git submodule update ถ้า Repo ใช้ Submodule และต้องมี git อยู่แล้วตอน Clone.

² chezmoi เป็น Binary ไฟล์เดียว ไม่ต้องมี Runtime แต่ข้ามเครื่องยังต้องมี git เพื่อ Clone Repo ไม่ได้ใช้ chezmoi init

³ Nix คือ Platform ทั้งชุด ไม่มีชื่อ Runtime สั้นกว่านั้นแบบ Perl หรือ Python

⁴ ⚠️ ในแถวนี้แปลว่าไม่ได้เทียบเนื้อไฟล์ใน $HOME ก่อน Apply. ตารางของ chezmoi ให้ dotbot เป็น ❌ ผมให้ ⚠️ เพราะ --dry-run บอกได้ว่าจะสร้าง Link อะไรบ้าง แต่ไม่ได้เทียบเนื้อไฟล์. stow -n ก็เป็นแบบเดียวกัน. Nix home-manager ไม่มีคำสั่ง Diff ของ $HOME. home-manager build แค่ Build Activation Package ให้เปิดดู.

⁵ home-manager ดึง Secret จาก Password Manager ไม่ได้. sops-nix ถอดรหัสไฟล์ตอน Activation แต่ไม่ได้ดึงค่าจาก Password Manager. พื้นฐานของ SOPS อยู่ใน SOPS: Secrets OPerationS

⁶ ตารางของ chezmoi ให้ Bare Git เป็น ⁉️ คือทำได้แต่ต้องลงแรงเอง ไม่มี Template ในตัว ทางที่ใช้บ่อยคือแยก Branch หรือแยกไฟล์ แล้วรวมส่วนที่ใช้ร่วมกันด้วยมือ

chezmoi

chezmoi เป็น Binary ตัวเดียว เก็บไฟล์ต้นทางไว้ใน Repo แล้วเขียนเป็นไฟล์จริงลง $HOME โดยค่าเริ่มต้นไม่ได้ทำ Symlink กลับไปที่ Repo ไฟล์ไหนต้องต่างกันต่อเครื่องก็ต่อท้ายชื่อด้วย .tmpl แล้วเขียน Go Template ในไฟล์นั้น

ตัวอย่างจาก dot_zshrc.tmpl ของผม (ตัดบรรทัดอื่นในแต่ละเงื่อนไขออก)

{{- if eq .chezmoi.os "darwin" }}
eval "$(/opt/homebrew/bin/brew shellenv)"
{{- else }}
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
{{- end }}

.zshrc ไฟล์เดียว Mac ได้ Path ของ Homebrew บน Apple Silicon ส่วน WSL ได้ Path ของ Linuxbrew ไม่ต้องแตก Branch

เครื่องที่ Setup แล้ว

ใช้ chezmoi update ซึ่งจะ Pull แล้ว Apply ให้ ถ้าอยากดูก่อนว่าจะเปลี่ยนอะไร ค่อยรัน chezmoi diff

Secret

chezmoi ดึง Secret ตอน Apply ผ่าน Template Function ของ Password Manager ค่าจริงไม่ได้อยู่ใน Repo ตัวอย่างจาก Docs คือ .zshrc.tmpl ที่ดึง Cloudflare API Token จาก 1Password

# set up $PATH
# …

# Cloudflare API Token retrieved from 1Password for use with flarectl
export CF_API_TOKEN='{{ onepasswordRead "op://Personal/cloudflare-api-token/password" }}'

# set up aliases and useful functions

Credit: Password Manager Integration

Package

Script ที่ชื่อขึ้นต้นด้วย run_ จะถูกรันตอน chezmoi apply เรียงตามชื่อไฟล์ run_onchange_ รันใหม่เฉพาะตอนเนื้อ Script เปลี่ยน ถ้าใส่ไว้ใน .chezmoiscripts/ chezmoi จะไม่สร้างโฟลเดอร์นั้นใน $HOME

ตัวอย่างจาก Docs คือ run_onchange_install-packages.sh.tmpl ติดตั้ง ripgrep คนละคำสั่งตาม OS

{{ if eq .chezmoi.os "linux" -}}
#!/bin/sh
sudo apt install ripgrep
{{ else if eq .chezmoi.os "darwin" -}}
#!/bin/sh
brew install ripgrep
{{ end -}}

Credit: Use scripts to perform actions

สิ่งนี้คือช่วยให้ผมสามารถ Install Package จบใน Repo นี้ได้เลย รายการ Package อยู่กับ Config ใน Repo เดียวกัน เครื่องใหม่รัน chezmoi apply ครั้งเดียว ได้ทั้งไฟล์ Config และคำสั่งติดตั้ง แก้ Script เพื่อเพิ่ม Package แล้ว run_onchange_ จะรันใหม่ให้ ตัวที่ติดตั้งจริงยังเป็น Package Manager อย่าง apt หรือ brew chezmoi เป็นตัวเรียกคำสั่งนั้น Script เดียวกันเรียกฝั่ง Windows ได้ด้วย เช่น สั่ง winget ผ่าน powershell.exe จาก WSL รายการของ Linux, macOS และ Windows เลยจบใน Repo นี้ที่เดียว

สิ่งที่ต้องแลก

คือชื่อไฟล์ ใน Repo .zshrc ชื่อ dot_zshrc.tmpl และไฟล์ที่ต้อง Execute ได้ขึ้นต้นด้วย executable_ ช่วงแรกต้องแปลชื่อในหัวทุกครั้งที่เปิด Repo

Harness

.cursor/ กับ .agents/ คือ Harness ที่เล่าในบทความที่แล้ว มันอยู่ใน Home Directory เหมือน Config อื่น เครื่องใหม่ต้องได้ของชุดนี้ด้วย

Skill, Rule, Hook และ MCP ที่มีอยู่ใน Marketplace ไม่ได้ก็อปทั้งก้อนเข้า ผมใช้ Repo Script ของ chezmoi เป็นตัวติดตั้งให้ตอน Apply และสำหรับของที่ผมเขียนเอง รวมทั้ง Hook กับ MCP ของผม แยกเก็บใน Dotfiles Repo กับ Config อื่น ๆ

เลือกตามโจทย์

  • มีเครื่องเดียว Bare Git ก็พอ
  • ชอบ Symlink และใช้ OS เดียว Stow อ่านง่ายกว่า
  • ดูแลเครื่องเป็นฝูงอยู่แล้ว Ansible คุ้มกว่า
  • อยากให้ทั้ง Environment Reproducible ทุกเครื่อง Nix ตอบโจทย์ตรงกว่า

ของผมคือหลายเครื่อง หลาย OS และอยาก Sync แค่ Config เลยจบที่ chezmoi

เจอกันใหม่บทความหน้าครับ

— EOF —

Post feedback

~/feedback
…

Comments

~/shortcuts

> Keyboard shortcuts

  • Toggle this help?H
  • Back to topT
  • Hold to scroll downJ
  • Hold to scroll upK
  • Forward (newer post or next page)F
  • Back (older post or previous page)B
  • Close helpEsc

Navigation

  • Blog (post list)L
  • AboutA
  • PlayP
  • Tags,