tmux on WSL2
คู่มืออ้างอิง tmux บน Windows 11 + WSL2 (Ubuntu) — mental model, ติดตั้ง, basics, config, workflow templates, integration, cheat sheet, troubleshooting
สารบัญ
เชี่ยวชาญ tmux บน WSL2 — single source of truth (tmux + WSL2 Handbook · reference · 9 sections) · คู่มืออ้างอิงส่วนตัวสำหรับใช้ tmux บน Windows 11 + WSL2 (Ubuntu) — workflow ของ data analyst + freelance web dev เน้นเข้าใจ mental model แล้วกลับมาเปิดดูได้ตลอด
- Stack: WSL2 · Claude Code · dbt · PostgreSQL · Next.js · Docker
- ทดสอบกับ: Ubuntu 22.04 / 24.04 · tmux 3.2a / 3.4
- Prefix:
Ctrl+a
Concept พื้นฐาน#
WSL2 คืออะไร
WSL2 (Windows Subsystem for Linux v2) คือ Linux kernel จริง ที่รันใน lightweight virtual machine ของ Hyper-V บน Windows 11 ทำให้รัน distro อย่าง Ubuntu, Debian, Fedora ได้แบบ native ภายใน Windows โดยไม่ต้อง dual boot และไม่ต้องเปิด VirtualBox/VMware แยก
| ประเด็น | WSL1 | WSL2 |
|---|---|---|
| สถาปัตยกรรม | แปลง syscall Linux → Windows NT (translation layer) | Linux kernel จริงใน Hyper-V VM |
| ความเข้ากันได้ | บาง syscall ไม่รองรับ (Docker daemon รันไม่ได้) | รองรับ Linux เกือบ 100% — Docker, systemd ใช้ได้ |
| Disk I/O (ใน Linux FS) | ช้า | เร็วกว่า WSL1 หลายเท่า |
Disk I/O (ข้าม /mnt/c/) | เร็วกว่า WSL2 | ช้ามาก (ผ่าน 9P protocol) |
| RAM | ใช้ตามจริง | จองล่วงหน้า ปรับ cap ได้ใน .wslconfig |
| Network | ใช้ stack ของ Windows ตรง ๆ | virtual NIC แยก + port forwarding อัตโนมัติ |
✅ สรุป — WSL2 เป็นค่า default ปัจจุบันและเป็นตัวเลือกที่ถูกต้องสำหรับ dev workflow ทั้งหมด ยกเว้นกรณีพิเศษที่ต้องการ I/O cross-OS เร็ว ๆ (เกือบไม่มีในงานจริง)
tmux คืออะไร
tmux (terminal multiplexer) คือโปรแกรมที่ให้รัน terminal session หลายอันใน window เดียว และที่สำคัญที่สุดคือ session ทำงานต่อใน background ได้แม้ปิด terminal ไปแล้ว — กลับมา tmux attach ก็ได้ทุก process ที่รันอยู่กลับคืน
tmux ต่างจาก Windows Terminal tab ยังไง
| ประเด็น | Windows Terminal tab/pane | tmux |
|---|---|---|
| Persistence | ปิด terminal = process ตายหมด | detach แล้ว process ยังรันใน background |
| Remote/SSH | tab อยู่ฝั่ง Windows เท่านั้น | session อยู่ฝั่ง server — SSH ขาดแล้ว reconnect ทำงานต่อได้ |
| Layout templates | ทำไม่ได้ | scriptable ผ่าน shell หรือ tmuxinator/tmuxp |
| Keybinding | จำกัด | rebind ได้ทั้งหมด |
| Cross-platform | Windows only | ทุก Unix-like ใช้ config เดียวกันได้ |
ใช้คู่กัน: Windows Terminal เป็น "หน้าต่าง" → เปิด WSL2 → ใน WSL2 รัน tmux เพื่อจัดการ session/window/pane
Mental Model 4 ชั้น
นี่คือแบบจำลองที่ทำให้ทุกอย่างที่เหลือเข้าใจง่าย — tmux ซ้อนกัน 4 ชั้นจากนอกสุดเข้าใน:
server → session → window → pane · server เกิดอัตโนมัติเมื่อสั่ง tmux ครั้งแรก ดับเมื่อทุก session หาย · pane คือ shell จริงที่รันโปรแกรม| ชั้น | ทำหน้าที่อะไร | analogy |
|---|---|---|
| server | daemon ดูแลทุก session — เกิดเมื่อสั่ง tmux ครั้งแรก ดับเมื่อทุก session หาย | บริการระบบ |
| session | หน่วยใหญ่สุด — ตั้งชื่อได้ (kmutt, nextjs) detach/attach อิสระ | "โปรเจกต์" |
| window | เหมือน tab — มีหมายเลข ตั้งชื่อได้ แต่ละ window มี layout pane ของตัวเอง | "tab" |
| pane | terminal จริง — รัน shell หนึ่งตัว split window เป็นหลายส่วน | "split" |
ทำไมต้องใช้ tmux + WSL2 คู่กัน
| ปัญหาที่แก้ | tmux ช่วยอะไร |
|---|---|
| Windows Terminal crash / restart เครื่อง | session ใน tmux ยังอยู่ (ตราบใดที่ WSL2 ยังไม่ shutdown) |
| ต้องเปิดหลาย tab สำหรับโปรเจกต์เดียว | จัด pane ใน window เดียว เห็นทุกอย่างพร้อมกัน |
| ลืมว่าทำอะไรไว้เมื่อวาน | attach กลับ ได้ทุก process + history + layout เดิม |
| Long-running job (dbt run, npm build) | detach ทิ้งไว้ — ไม่ต้องคา terminal เปิดทั้งวัน |
| ต้องโชว์งาน 2 หน้าจอ | window สำหรับ code, อีก window สำหรับ logs/db — สลับด้วย keystroke |
การติดตั้ง#
2.1 เช็กก่อนว่ามี WSL2 อยู่หรือยัง
เปิด PowerShell (ไม่ต้อง admin):
wsl -l -vผลลัพธ์ที่เป็นไปได้:
- ขึ้น list distro พร้อม
VERSION 2→ มี WSL2 แล้ว ข้ามไป 2.3 - ขึ้น list แต่
VERSION 1→ ดู 2.4 (migrate) - error
...has no installed distributions→ ทำ 2.2 - error เกี่ยวกับ feature → ดู 2.4 (troubleshoot virtualization)
2.2 ติดตั้ง WSL2 + Ubuntu
เปิด PowerShell แบบ Run as Administrator แล้ว:
wsl --installคำสั่งเดียวนี้จะ (อัตโนมัติ): เปิด feature VirtualMachinePlatform + Microsoft-Windows-Subsystem-Linux · ตั้ง default WSL version = 2 · download + install Ubuntu · ขอ reboot
หลัง reboot, Ubuntu จะเปิดเองและขอตั้ง username/password (UNIX account ไม่ใช่ Microsoft account) ถ้าอยากเลือก distro อื่น:
wsl --list --online # ดู distro ที่ install ได้
wsl --install -d Ubuntu-24.042.3 ตั้ง default version + update
wsl --set-default-version 2
wsl --update
wsl -l -vควรเห็น STATE = Running/Stopped และ VERSION = 2
2.4 Edge case: virtualization ปิดใน BIOS / ยังติด WSL1
Error เกี่ยวกับ Hyper-V/virtualization: เช็ก Task Manager → Performance → CPU → บรรทัด "Virtualization" ต้องเป็น Enabled ถ้า Disabled รีสตาร์ทเข้า BIOS/UEFI (กด F2/Del/F10 ตอนบูต) หาเมนู Intel VT-x / AMD-V / SVM Mode → enable → save → exit → กลับมารัน wsl --install ใหม่ ถ้ายังไม่ได้ เปิด feature ด้วยมือ (PowerShell as admin) แล้ว reboot:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestartยังติด WSL1 (wsl -l -v ขึ้น VERSION 1): migrate ด้วยคำสั่งเดียว ใช้เวลา 5–30 นาทีตามขนาด disk image รอจน Conversion complete. แล้วเช็กซ้ำ wsl -l -v:
wsl --set-version Ubuntu 22.5 ลง tmux ใน Ubuntu
$ sudo apt update
$ sudo apt install -y tmux
$ tmux -Vควรเห็น tmux 3.x.x ขึ้นไป — Ubuntu 22.04+ ได้ 3.2a เป็น default ซึ่งพอใช้ feature ในคู่มือนี้
2.6 เริ่มใช้ครั้งแรก
$ tmux # เปิด session ไม่ตั้งชื่อ
$ tmux new -s test # เปิด session ชื่อ "test"ถ้าเห็นแถบสถานะสีเขียวด้านล่าง = ทำงานแล้ว ออกด้วย Ctrl+d หรือพิมพ์ exit
tmux Basics#
3.1 Prefix Key Concept
ทุก keybinding ใน tmux ต้องกด prefix key ก่อน แล้วตามด้วย key สั่ง คล้าย leader key ใน Vim หรือ Ctrl+x ใน Emacs
| Prefix | ที่มา | ข้อดี | ข้อเสีย |
|---|---|---|---|
Ctrl+b | default ของ tmux | ไม่ชน Emacs convention | กดยาก (สองมือ), ไกลจากเหย้า |
Ctrl+a | จาก GNU Screen | กดง่าย, ergonomic | ชนกับ bash "ไปต้นบรรทัด" |
🧭 Why Ctrl+a — กดด้วยมือซ้ายเดียว (นิ้วก้อย + นิ้วชี้) ลดอาการล้า keystroke ที่ใช้บ่อยที่สุดหลังลง tmux — ตลอดคู่มือนี้
prefix=Ctrl+aและprefix + XหมายถึงกดCtrl+aปล่อย แล้วกดX
3.2 Session
| Action | Command | Keybinding |
|---|---|---|
| สร้าง session (ไม่ตั้งชื่อ) | tmux | — |
| สร้าง session + ตั้งชื่อ | tmux new -s NAME | prefix + S (custom) |
| Detach (ออกแต่ยังรัน) | — | prefix + d |
| List session | tmux ls | prefix + s (picker) |
| Attach ล่าสุด | tmux a | — |
| Attach ที่ระบุ | tmux a -t NAME | — |
| Rename session | — | prefix + $ |
| Kill session ที่ระบุ | tmux kill-session -t NAME | prefix + K (custom) |
| Kill ทุก session | tmux kill-server | — |
Workflow ปกติ
$ tmux new -s kmutt # เริ่มงาน
# ... ทำงาน ... กด prefix + d เพื่อ detach (session ยังรัน) ปิด terminal ได้เลย
# กลับมาทีหลัง:
$ tmux ls # ดูว่ามี session อะไรอยู่
$ tmux a -t kmutt # attach กลับ — ทุกอย่างยังอยู่3.3 Window
Window คือ "tab" ภายใน session — มีหมายเลข (เริ่มที่ 1 ตาม config) และตั้งชื่อได้
| Action | Keybinding |
|---|---|
| สร้าง window ใหม่ | prefix + c |
| ไป window N (1..9) | prefix + N |
| window ถัดไป / ก่อนหน้า | prefix + n / prefix + p |
| window ล่าสุด (toggle) | prefix + Tab (custom) |
| list window แบบ picker | prefix + w |
| rename window | prefix + , |
| find window ตามชื่อ | prefix + f |
| kill window | prefix + & (confirm) |
3.4 Pane
Pane คือการ split window เป็นหลายช่อง terminal
| Action | Keybinding |
|---|---|
| Split vertical (ขวา) | prefix + | (custom) |
| Split horizontal (ล่าง) | prefix + - (custom) |
| ย้าย pane ซ้าย/ล่าง/บน/ขวา | prefix + h/j/k/l (custom) |
| สลับ pane ล่าสุด | prefix + ; |
| Zoom pane (toggle เต็มจอ) | prefix + z |
| Resize ซ้าย/ล่าง/บน/ขวา | prefix + H/J/K/L (custom, repeat) |
| Cycle layout | prefix + Space |
| แสดงหมายเลข pane | prefix + q |
| Convert pane เป็น window | prefix + ! |
| Kill pane | prefix + X (custom, confirm) |
🧭 Why split ด้วย
|กับ-— mnemonic จำง่าย —|คือเส้นแบ่งแนวตั้ง,-คือเส้นแบ่งแนวนอน ต่างจาก default%กับ"ที่ไม่สื่อทิศทาง
3.5 Copy Mode
Copy mode คือโหมดอ่าน — เลื่อนดู scrollback, ค้นหา, คัดลอกข้อความ
| Action | Keybinding |
|---|---|
| เข้า / ออก copy mode | prefix + [ / q หรือ Esc |
| เลื่อน บรรทัด / ตัวอักษร | k j / h l (vi) |
| page up/down · บนสุด/ล่างสุด | Ctrl+u/Ctrl+d · g/G |
| ค้นหา fwd / bwd | /PATTERN / ?PATTERN |
| ผลถัดไป / ก่อนหน้า | n / N |
| เริ่ม selection | v (custom — vi visual) |
| Rectangle (block) selection | Ctrl+v (ต้อง bind เพิ่ม — ดูหมายเหตุ) |
| Copy → Windows clipboard | y (custom — pipe ไป clip.exe) |
| Paste จาก tmux buffer | prefix + ] |
Rectangle selection (เลือกเป็นสี่เหลี่ยม)
tmux default ผูก v ไว้กับ rectangle-toggle อยู่แล้ว แต่ config ในคู่มือนี้ (4.3) rebind v เป็น begin-selection แบบ Vim visual mode ปกติแทน — อยากเลือกเป็นสี่เหลี่ยม (เช่นตัดคอลัมน์เดียวจาก psql output หรือ log ที่มี timestamp นำหน้าทุกบรรทัด) ต้องเพิ่ม binding เอง:
bind-key -T copy-mode-vi C-v send-keys -X rectangle-toggleเข้า copy mode ตามปกติ (prefix + [) ขยับ cursor ไปจุดเริ่ม กด Ctrl+v แทน v แล้วขยับไปมุมตรงข้าม — ที่เหลือ (y yank) ใช้เหมือน selection ปกติ
Export scrollback ทั้งหมดออกไฟล์
เอาไว้เก็บ log ยาว ๆ (เช่น dbt run ทั้ง run, error stack trace เก่า) ออกจาก tmux ไปเป็นไฟล์ธรรมดา ค้นด้วย grep หรือส่งให้คนอื่นได้ ไม่ต้องเข้า copy mode เลือกด้วยมือทีละบรรทัด:
$ tmux capture-pane -pS - -J > ~/scrollback.txt-p พ่นออก stdout แทนที่จะเก็บใน tmux buffer · -S - เริ่มจากบรรทัดแรกสุดของ history ทั้งหมด (ไม่ใช่แค่จอที่เห็น) · -J รวมบรรทัดที่ wrap กลับเป็นบรรทัดเดียวก่อน · รันจากนอก tmux ก็ได้ ไม่ต้องเข้า copy mode ก่อน
Configuration#
ไฟล์ tmux.conf ที่แนบใน repo มี comment อธิบายทุกบรรทัด
4.1 ติดตั้ง config
# จาก WSL2 (สมมติ clone repo นี้มาที่ ~/tmux-wsl2-handbook/)
$ cp ~/tmux-wsl2-handbook/tmux.conf ~/.tmux.conf
# หรือ symlink (อัพเดต config ผ่าน git pull ได้ทันที)
$ ln -s ~/tmux-wsl2-handbook/tmux.conf ~/.tmux.conf4.2 Reload config
ถ้าอยู่ใน tmux session อยู่แล้ว ไม่ต้องปิด ใช้ keybinding ที่ตั้งไว้: prefix + r → จะ source ~/.tmux.conf ใหม่ + แสดง tmux.conf reloaded ใน status bar
ถ้าอยู่นอก tmux (หรือ binding ยังไม่ทำงานเพราะเพิ่ง install):
$ tmux source-file ~/.tmux.conf4.3 สรุปสิ่งที่ config นี้เปลี่ยน
| หมวด | ค่า | เหตุผล |
|---|---|---|
| Prefix | Ctrl+a (จาก Ctrl+b) | กดง่ายกว่า, มือเดียว |
| Window/pane numbering | เริ่มที่ 1 | ตรงกับลำดับปุ่ม 1..9 |
| Renumber on close | on | ไม่มีรู gap หลังลบ window |
| Mouse | on | scroll, resize, click pane ได้ |
| Scrollback | 50,000 บรรทัด | logs dbt/docker ยาว ๆ ไม่หาย |
| Escape time | 10ms | Neovim ไม่ค้างหลังกด Esc |
| Split bindings | | และ - | mnemonic |
| Navigate / Resize panes | h/j/k/l · H/J/K/L | ตรงกับ Vim |
| Copy mode | vi keys, yank → clip.exe | คัดลอกตรงเข้า Windows clipboard |
| Default terminal | tmux-256color + RGB | สีของ Neovim/bat ถูกต้อง |
Workflow Templates#
ทุก template สร้าง session พร้อม pane layout ครบในคำสั่งเดียว ใช้ shell command ปกติ (ไม่ต้องลง plugin) — ก๊อปวางใน WSL2 ได้เลย
5.1 Pattern ทั่วไป: 3-pane Dev Session (editor/agent · run · db)
โครงนี้ใช้ได้กับแทบทุก dev stack — pane 1 รัน editor หรือ coding agent เต็มความสูงซ้าย, pane 2 รัน dev server/build/test watcher, pane 3 รัน db client หรือ log viewer แทนที่ placeholder ด้วยคำสั่งจริงของโปรเจกต์คุณ:
PROJECT_DIR="$HOME/projects/my-project"
SESSION="myproject"
# pane 1 — editor/agent เต็มความสูงซ้าย
tmux new-session -d -s "$SESSION" -n main -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION:main" 'YOUR_EDITOR_OR_AGENT_CMD' C-m
# pane 2 — ขวาบน: dev server / build / test watcher
tmux split-window -h -t "$SESSION:main" -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION:main.2" 'YOUR_RUN_CMD' C-m
# pane 3 — ขวาล่าง: db client / logs
tmux split-window -v -t "$SESSION:main.2" -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION:main.3" 'YOUR_DB_OR_LOG_CMD' C-m
tmux select-pane -t "$SESSION:main.1"
tmux attach -t "$SESSION"สองตัวอย่างประยุกต์ด้านล่าง (KMUTT DataPlatform, Next.js) คือ pattern เดียวกันนี้เป๊ะ ๆ แค่ใส่คำสั่งจริงของแต่ละ stack — ก๊อปโครงนี้ไปสลับ 3 คำสั่งก็ใช้กับโปรเจกต์ใหม่ได้ทันที
5.2 ตัวอย่างประยุกต์: KMUTT DataPlatform session
เป้าหมาย: 1 session, 2 window — Window 1 dbt: 3 pane (Claude Code ซ้ายเต็มความสูง · dbt ขวาบน · psql ขวาล่าง) · Window 2 logs: ดู docker logs
kmutt session · Claude เต็มความสูงซ้าย, dbt/psql ซ้อนขวา, logs แยก window — เก็บเป็น ~/bin/kmutt-up.sh เรียกครั้งเดียวจบ# ปรับ path ตามจริง
PROJECT_DIR="$HOME/projects/kmutt-dataplatform"
PG_USER="postgres"; PG_DB="dataplatform"
# สร้าง session + window แรก ที่ pane เริ่มต้นรัน claude
tmux new-session -d -s kmutt -n dbt -c "$PROJECT_DIR"
tmux send-keys -t kmutt:dbt 'claude' C-m
# split ขวา (pane 2) สำหรับ dbt
tmux split-window -h -t kmutt:dbt -c "$PROJECT_DIR"
tmux send-keys -t kmutt:dbt.2 'source .venv/bin/activate && dbt debug' C-m
# split ขวาล่าง (pane 3) สำหรับ psql
tmux split-window -v -t kmutt:dbt.2 -c "$PROJECT_DIR"
tmux send-keys -t kmutt:dbt.3 "psql -h localhost -U $PG_USER -d $PG_DB" C-m
# window 2 — logs
tmux new-window -t kmutt -n logs -c "$PROJECT_DIR"
tmux send-keys -t kmutt:logs 'docker compose logs -f' C-m
# focus กลับ window แรก pane Claude แล้ว attach
tmux select-window -t kmutt:dbt; tmux select-pane -t kmutt:dbt.1
tmux attach -t kmuttเก็บเป็น script ~/bin/kmutt-up.sh แล้ว chmod +x — เรียกครั้งเดียวจบ
5.3 ตัวอย่างประยุกต์: Next.js Freelance Project
เป้าหมาย: Window 1 dev: 4 pane (Claude / npm run dev / git / db client) · Window 2 prisma: prisma studio
PROJECT_DIR="$HOME/projects/freelance-app"
tmux new-session -d -s nextjs -n dev -c "$PROJECT_DIR"
tmux send-keys -t nextjs:dev 'claude' C-m # pane 1 ซ้ายบน
tmux split-window -h -t nextjs:dev -c "$PROJECT_DIR"
tmux send-keys -t nextjs:dev.2 'npm run dev' C-m # pane 2 ขวาบน
tmux split-window -v -t nextjs:dev.1 -c "$PROJECT_DIR"
tmux send-keys -t nextjs:dev.3 'git status' C-m # pane 3 ซ้ายล่าง
tmux split-window -v -t nextjs:dev.2 -c "$PROJECT_DIR"
tmux send-keys -t nextjs:dev.4 'pgcli postgres://app:app@localhost:5432/app' C-m
tmux new-window -t nextjs -n prisma -c "$PROJECT_DIR"
tmux send-keys -t nextjs:prisma 'npx prisma studio' C-m
tmux select-window -t nextjs:dev; tmux select-pane -t nextjs:dev.1
tmux attach -t nextjsหลัง npm run dev ขึ้นมาแล้ว เปิด Chrome บน Windows ที่ http://localhost:3000 — WSL2 forward port ให้อัตโนมัติ (ดู 6.4)
5.4 Daily Standup Workflow
ใช้ tmux ให้ครบ cycle ของวัน — เลิกงาน detach, วันถัดไป attach กลับ
| ช่วงเวลา | ทำอะไร |
|---|---|
| เช้า (วันแรก) | ~/bin/kmutt-up.sh หรือ ~/bin/nextjs-up.sh |
| ไปทานข้าว/ประชุม | prefix + d (detach — ทุกอย่างยังรัน) ปิด Windows Terminal/ฝา laptop ได้ |
| กลับมาทำต่อ | tmux ls → tmux a -t kmutt |
| เลิกงาน | prefix + d · อย่ารัน wsl --shutdown |
| สิ้นสัปดาห์ clean | tmux kill-session -t nextjs หรือ tmux kill-server |
WSL2 ↔ Windows Integration#
6.1 Filesystem: /home/ vs /mnt/c/
/mnt/c/ เพราะ npm install, git status, dbt run จะช้าจนใช้ไม่ได้ถ้าโปรเจกต์เดิมอยู่ใน /mnt/c/Users/.../ ย้ายเข้า Linux FS:
$ mkdir -p ~/projects
$ mv /mnt/c/Users/STUDENT/Documents/myproject ~/projects/6.2 เข้าถึงไฟล์ WSL จาก Windows
ใน File Explorer พิมพ์ใน address bar: \\wsl$\Ubuntu\home\<user>\ หรือบน Windows 11: \\wsl.localhost\Ubuntu\home\<user>\ · ทางลัดจาก WSL terminal:
$ explorer.exe . # เปิด Explorer ที่ folder ปัจจุบัน6.3 VS Code Remote-WSL
Install (ครั้งเดียว): ลง VS Code บน Windows → ติดตั้ง extension "WSL" (Microsoft) → restart · ใช้งาน จาก WSL terminal:
$ cd ~/projects/kmutt-dataplatform
$ code .ครั้งแรก VS Code จะ install vscode-server ใน WSL อัตโนมัติ — extension (Python, Node, debugger) รันฝั่ง Linux ส่วน UI ฝั่ง Windows · เช็กมุมล่างซ้ายขึ้น WSL: Ubuntu
6.4 Network: localhost Port Forwarding
Windows 11 + WSL2 รุ่นใหม่มี localhost relay อัตโนมัติ — เปิด server ใน WSL ที่ 0.0.0.0:3000 แล้วเปิด Chrome ฝั่ง Windows ที่ http://localhost:3000 เห็นทันที
6.5 เปิด Windows app จาก WSL
| คำสั่งใน WSL | ทำอะไร |
|---|---|
explorer.exe . | เปิด File Explorer ที่ folder ปัจจุบัน |
code . | เปิด VS Code Remote-WSL |
clip.exe < file.txt | copy เนื้อหาไฟล์เข้า Windows clipboard |
cmd.exe /c start https://... | เปิด URL ด้วย default browser |
wslview <file> | เปิดด้วย default Windows app (เหมือน xdg-open) |
Cheat Sheet#
🧭 หน้าเดียวจบ — ครอบคลุม 80% ของการใช้งานประจำวัน พิมพ์แปะข้างจอได้ · Prefix =
Ctrl+a(กดแล้วปล่อย ตามด้วย key สั่ง)
Top 10 Commands (จากนอก tmux)
| # | คำสั่ง | ทำอะไร |
|---|---|---|
| 1 | tmux new -s NAME | สร้าง session ใหม่พร้อมตั้งชื่อ |
| 2 | tmux ls | ดูว่ามี session อะไรอยู่ |
| 3 | tmux a | attach session ล่าสุด |
| 4 | tmux a -t NAME | attach session ที่ระบุ |
| 5 | tmux kill-session -t NAME | ลบเฉพาะ session |
| 6 | tmux kill-server | ลบ tmux ทั้งหมด |
| 7 | tmux source ~/.tmux.conf | reload config จากนอก tmux |
| 8 | tmux -V | เช็กเวอร์ชัน |
| 9 | wsl -l -v | (PowerShell) เช็กสถานะ WSL distro |
| 10 | wsl --shutdown | (PowerShell) shutdown WSL ทั้งหมด — ใช้ตอน RAM บวม |
Troubleshooting#
8.1 tmux session ค้าง, ต้อง kill ทุก session
$ tmux kill-server
# ถ้า tmux ls / tmux a ค้าง:
$ pkill -9 tmux # ฆ่า process ทั้งหมด
$ rm -rf /tmp/tmux-$(id -u) # ลบ socket directory
$ tmux ls # ควรเห็น "no server running"8.2 Config ไม่ reload หลังแก้
# 1. ทดสอบ syntax (ถ้า error จะ print ออกมา)
$ tmux source-file ~/.tmux.conf
# 2. เช็คว่า binding ถูก register หรือยัง
$ tmux list-keys | grep -i "source-file"
# 3. อยู่ใน session ที่ start ก่อนเพิ่ม binding? → ใช้คำสั่งใน 1 หรือ kill-server แล้วเปิดใหม่8.3 WSL2 กิน RAM เยอะเกินไป
WSL2 จอง RAM แบบ dynamic แต่ไม่คืนให้ Windows ทันที — สร้าง C:\Users\<WindowsUser>\.wslconfig:
[wsl2]
memory=8GB # cap RAM (เครื่อง 16GB ใช้ 8GB, 32GB ใช้ 16GB)
processors=4 # cap จำนวน vCPU
swap=4GB
localhostForwarding=trueแล้วบังคับ apply: wsl --shutdown → เปิด WSL ใหม่ · .wslconfig มีผลทุก distro ภายใต้ WSL2
8.4 ใช้ wsl --shutdown ตอนไหน
| ใช้เมื่อ ✓ | ห้ามใช้เมื่อ ✗ |
|---|---|
เพิ่งแก้ .wslconfig ต้องการ apply | มี tmux session ที่ยังไม่อยากให้ตาย (จะถูก SIGKILL ทันที) |
| RAM ของ WSL2 บวม ต้องการคืนให้ Windows | กำลังมี long-running job (dbt seed, npm build) → งานพัง |
| WSL2 hang / network เพี้ยน |
8.5 Port ใน WSL ไม่ถึง Windows browser
# 1. Server bind ที่อะไร?
$ ss -tlnp | grep 3000
# ต้องเห็น 0.0.0.0:3000 หรือ *:3000 — ถ้า 127.0.0.1:3000 ให้แก้ framework ให้ bind 0.0.0.0
# 2. Firewall block? (PowerShell as admin, ทดสอบชั่วคราว)
New-NetFirewallRule -DisplayName "WSL2 dev" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 3000
# 3. รีสตาร์ท WSL
wsl --shutdown
# 4. Windows 11 บางเวอร์ชันมี bug — เพิ่ม manual port proxy (admin)
$wslIp = (wsl hostname -I).Trim().Split()[0]
netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=$wslIp8.6 Clock skew หลัง laptop sleep
อาการ: หลัง wake from sleep ใช้ git, npm, apt, JWT auth พัง เพราะเวลาใน WSL2 ห่างจาก Windows หลายชั่วโมง
# แก้ทันที
$ sudo hwclock -s # อ่านเวลาจาก hardware clock (= Windows)
# แก้ถาวร — systemd-timesyncd (Ubuntu 22.04+ มีให้แล้ว)
$ sudo apt install -y systemd-timesyncd
$ sudo systemctl enable --now systemd-timesyncd
$ timedatectl status8.7 Nested tmux: prefix ชนกันหลัง SSH เข้า remote ที่มี tmux อยู่แล้ว
อาการ: อยู่ใน tmux ฝั่ง WSL2 (local) อยู่แล้ว แล้ว SSH ออกไปที่ remote server ที่ auto-attach tmux ทันทีที่ login (ดู 9.1) — กด prefix (Ctrl+a) กี่ครั้งก็ไปโดน tmux ฝั่ง local เสมอ สั่งงาน tmux ฝั่ง remote (inner) ตรง ๆ ไม่ได้เลย เพราะทั้งสองฝั่งแย่ง keystroke เดียวกัน
ทางแก้ที่มีอยู่แล้วในคู่มือนี้: config ใน 4.3 ผูก prefix + Ctrl+a ไว้กับ send-prefix อยู่แล้ว — กด Ctrl+a สองครั้งติด จะส่ง Ctrl+a ตัวเดียวทะลุเข้าไปหา tmux ฝั่ง remote แทนที่จะโดน local สกัดไว้
ทางแก้ที่ง่ายกว่า: ตั้ง prefix ของ tmux บน remote server ให้ต่างจาก local ไปเลย (เช่น ปล่อย remote ไว้ที่ default Ctrl+b ในเมื่อ local เปลี่ยนเป็น Ctrl+a) — ตัดปัญหาชนกันตั้งแต่ต้นทาง ไม่ต้องจำ double-tap
ทางแก้แบบ advanced (nested บ่อยจนคุ้ม): bind คีย์แยก (เช่น F12) ให้ปิด keybinding ของ local tmux ทั้งชุดชั่วคราว ปล่อยทุก keystroke ทะลุเข้า remote ตรง ๆ แล้วกดซ้ำเพื่อเปิดกลับ:
# ปิด keybinding ของ tmux ฝั่ง local ชั่วคราว (ทุกคีย์ทะลุเข้า remote)
bind -T root F12 \
set prefix None \;\
set key-table off \;\
refresh-client -S
# กด F12 ซ้ำเพื่อเปิด keybinding กลับ
bind -T off F12 \
set -u prefix \;\
set -u key-table \;\
refresh-client -S8.8 TERM / สีเพี้ยนหลัง SSH เข้า remote tmux
อาการ: Neovim/bat/lazygit สีเพี้ยนหรือ theme พังทันทีที่ SSH เข้า remote server (โดยเฉพาะที่รัน tmux ซ้อนอีกที) ทั้งที่ local ฝั่ง WSL2 สีถูกต้องปกติดี
สาเหตุ: remote host ไม่มี terminfo entry tmux-256color (หรือมีแบบเก่า/พัง) — tmux จะ fallback ไปใช้ screen-256color แทนแบบเงียบ ๆ ซึ่งตีความ escape sequence บางตัว (โดยเฉพาะ true-color/RGB) ต่างจาก tmux-256color
# เช็คว่า remote มี terminfo entry นี้หรือยัง (error "can't open terminfo file" = ไม่มี)
$ infocmp tmux-256color
# copy terminfo จากเครื่องที่มี (เช่น WSL2 นี้) ไปติดตั้งที่ remote โดยตรง
$ infocmp -x tmux-256color | ssh remote-host "tic -x -"ถ้าไม่สะดวกติดตั้ง terminfo บน remote (เช่นไม่มีสิทธิ์ compile) ให้ตั้งใน .tmux.conf ของ remote ให้ fallback ไป screen-256color แทน — ใช้ได้แทบทุกที่ แลกกับไม่รองรับ italic:
set -g default-terminal "screen-256color"ขั้นต่อไป (Advanced)#
เมื่อใช้ tmux จนคล่อง ค่อยลองเพิ่ม:
9.1 SSH เข้า Remote Server + tmux (session ที่รอดจาก disconnect)
จำแถว "Remote/SSH" ในตารางเปรียบเทียบ tmux ↔ Windows Terminal tab จาก Section 1 ได้ไหม — นี่คือหัวข้อนั้นแบบเต็ม และเป็น use case ดั้งเดิมที่สุดของ tmux ด้วย (ก่อนมี WSL2 ด้วยซ้ำ คนใช้ tmux กับ GNU Screen เพื่อเหตุผลนี้อย่างเดียว): SSH เข้า remote server แล้วรัน tmux ที่นั่น ถ้า SSH หลุด (เน็ตกระตุก, ปิดฝา laptop, เปลี่ยน network) session ฝั่ง server ยังรันต่อเนื่อง — SSH กลับเข้าไปใหม่แล้ว attach คืนได้ทุกอย่างเหมือนไม่มีอะไรเกิดขึ้น เพราะ tmux server เป็น process อิสระจาก SSH connection ที่ต่อเข้ามา ไม่เกี่ยวกับ WSL2 เลย — ใช้ได้กับ Linux server ตัวไหนก็ได้ที่ SSH ถึง
คำสั่งเดียวจบ — attach ถ้ามี session อยู่แล้ว สร้างใหม่ให้ถ้ายังไม่มี:
$ ssh -t user@remote-host "tmux new-session -A -s work"-A บน tmux new-session คือหัวใจ — ถ้า session ชื่อ work มีอยู่แล้วจะ behave เหมือน attach ถ้ายังไม่มีจะสร้างใหม่ให้เลย ไม่ต้องเช็กเองก่อน · -t บน ssh จำเป็นต้องมีเมื่อส่งคำสั่งต่อท้าย (บังคับ allocate pseudo-terminal ไม่งั้น tmux เรนเดอร์ไม่ถูก)
🧭 ทำให้สั้นลงอีก — ตั้ง Host alias ใน
~/.ssh/configแล้วผูกเป็น shell alias เช่นalias work='ssh -t remote-host "tmux new-session -A -s work"'— พิมพ์workคำเดียวจบทั้ง connect และ attach
เจอปัญหาเรื่อง prefix ชนกัน (ถ้าฝั่ง local รัน tmux อยู่ด้วย) หรือสีเพี้ยนหลัง SSH ดู 8.7–8.8
9.2 tpm (tmux Plugin Manager)
จัดการ plugin tmux ด้วย git ผ่าน 1 บรรทัดใน .tmux.conf
$ git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpmเพิ่มท้าย ~/.tmux.conf:
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
run '~/.tmux/plugins/tpm/tpm'Install plugin: prefix + I (capital i)
9.3 tmux-resurrect + tmux-continuum
ปัญหา: wsl --shutdown แล้ว session หายหมด ต้อง bootstrap ใหม่ · แก้: 2 plugin นี้ save/restore session อัตโนมัติทุก 15 นาที — เปิด tmux แล้วทุก window/pane กลับมาเลย
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
set -g @continuum-restore 'on'prefix + Ctrl+s save, prefix + Ctrl+r restore (manual)
9.4 tmuxinator / tmuxp
แทนที่ shell script ใน Section 5 ด้วย YAML declarative — config อ่านง่ายและ portable · tmuxinator (Ruby): mux kmutt · tmuxp (Python): tmuxp load nextjs
# ~/.tmuxp/kmutt.yaml
session_name: kmutt
start_directory: ~/projects/kmutt-dataplatform
windows:
- window_name: dbt
layout: main-vertical
panes:
- claude
- source .venv/bin/activate && dbt debug
- psql -h localhost -U postgres -d dataplatform
- window_name: logs
panes:
- docker compose logs -f9.5 Integration กับ Neovim
ปัญหา: navigate ระหว่าง Neovim split กับ tmux pane ต้องสลับวิธีกด — ใน Neovim split ใช้ Ctrl+w แล้วตามด้วยทิศทาง ส่วนใน tmux ต้องกด prefix ก่อนเสมอ (ดู 3.4) สองระบบไม่ยอมรับ key ชุดเดียวกัน christoomey/vim-tmux-navigator แก้ปัญหานี้ — กด Ctrl+h/j/k/l ชุดเดียวกันทั้งสองฝั่ง มัน "ฉลาด" พอจะรู้ว่า pane ที่ focus อยู่ตอนนี้เป็น Neovim split หรือ tmux pane แล้วส่ง keystroke ไปที่ถูกที่ — power-user setup ที่คุ้มสุด ๆ ถ้าใช้ Neovim เป็นหลัก
ฝั่ง Neovim (lazy.nvim)
-- lua/plugins/tmux-navigator.lua
return {
"christoomey/vim-tmux-navigator",
cmd = {
"TmuxNavigateLeft", "TmuxNavigateDown",
"TmuxNavigateUp", "TmuxNavigateRight",
},
keys = {
{ "<c-h>", "<cmd><C-U>TmuxNavigateLeft<cr>" },
{ "<c-j>", "<cmd><C-U>TmuxNavigateDown<cr>" },
{ "<c-k>", "<cmd><C-U>TmuxNavigateUp<cr>" },
{ "<c-l>", "<cmd><C-U>TmuxNavigateRight<cr>" },
},
}ฝั่ง tmux (.tmux.conf)
ต้องเพิ่ม "smart" pane switching — เช็กก่อนว่า pane ที่ focus อยู่กำลังรัน (neo)vim จริงไหม แล้วค่อยตัดสินใจว่าจะส่ง keystroke เข้าโปรแกรมหรือสลับ pane:
vim_pattern='(\S+/)?g?\.?(view|l?n?vim?x?|fzf)(diff)?(-wrapped)?'
is_vim="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +${vim_pattern}$'"
bind-key -n C-h if-shell "$is_vim" 'send-keys C-h' 'select-pane -L'
bind-key -n C-j if-shell "$is_vim" 'send-keys C-j' 'select-pane -D'
bind-key -n C-k if-shell "$is_vim" 'send-keys C-k' 'select-pane -U'
bind-key -n C-l if-shell "$is_vim" 'send-keys C-l' 'select-pane -R'อ่านต่อ: Harness Engineering#
tmux · WSL2 Handbook — คู่มืออ้างอิงฉบับเข้าใจถึงแก่น · single source of truth
Stack: WSL2 (Ubuntu) · Claude Code · dbt Core · PostgreSQL · Next.js · Docker · ทดสอบกับ Ubuntu 22.04/24.04, tmux 3.2a/3.4
config เต็ม: tmux.conf ที่แนบใน repo ของคู่มือ · ส่วนหนึ่งของชุด คู่มือฉบับเข้าใจถึงแก่น