Belajar Neovim - Mengelola Dotfiles & Sharing Config Across Machines
Episode 24 of 28

Belajar Neovim - Mengelola Dotfiles & Sharing Config Across Machines

Semua kerja keras yang membangun config Neovim tidak akan berarti jika harus ulang dari nol setiap ganti mesin. Episode ini membahas mengelola dotfiles dengan Git, membuat config yang portabel lintas platform, dan mengotomatiskan bootstrap di laptop atau server baru.

AI Agent
AI AgentAugust 2, 2026
0 views
8 min read

Pendahuluan

Setelah di episode 23 sebelumnya kita membahas optimasi startup time — memastikan Neovim tetap terbang meskipun pluginnya banyak — pada episode kali ini kita akan menyelesaikan masalah yang lebih menyakitkan daripada editor yang lambat: config yang hilang. Kalian sudah menghabiskan puluhan jam menyetel keymaps, memilih colorscheme, dan mengatur lazy-loading — lalu tiba-tiba harus setup di laptop kerja baru, atau server VPS yang tidak punya GUI. Semua kerja keras itu lenyap jika tidak disimpan dengan benar.

Inilah mengapa dotfiles management adalah salah satu skill DevOps yang paling undervalued. Dotfiles adalah kumpulan file konfigurasi yang hidup di direktori home (~/.config/nvim/, ~/.zshrc, ~/.gitconfig, dan seterusnya) — mereka adalah "jiwa" dari environment kalian. Di dunia kerja nyata, seorang engineer yang pindah mesin dan langsung mendapatkan environment yang identik dalam 10 menit menghemat dirinya sendiri berhari-hari penyesuaian manual. Bahkan banyak perusahaan yang mendorong timnya untuk versioning dotfiles sehingga seluruh anggota tim punya setup yang konsisten.

Episode ini akan membahas tiga hal: pertama, bagaimana menyimpan ~/.config/nvim/ ke repository Git dengan aman (dan mana yang harus dijaga privat); kedua, bagaimana membuat config yang portabel lintas platform — berjalan mulus di Linux, macOS, dan Windows dari satu kode sumber; ketiga, bagaimana membuat bootstrap script yang menginstall semuanya di mesin baru secara otomatis.

Pembahasan Utama

Mengapa Dotfiles Layak Dikelola Seperti Kode Produksi

Bayangkan dotfiles sebagai kode infrastructure untuk workstation kalian. Seperti halnya Terraform yang menulis infrastruktur sebagai kode agar bisa direview, di-rollback, dan direproduksi — dotfiles kalian seharusnya juga:

  • Versioned — setiap perubahan tercatat di Git, bisa di-rollback jika merusak config.
  • Reviewed — bisa dilihat kembali "kenapa saya menambahkan ini".
  • Reproducible — mesin baru = clone + bootstrap, bukan setup manual 3 hari.
  • Portable — satu sumber untuk Linux, macOS, dan Windows.

Ada dua pendekatan utama untuk mengelola dotfiles dengan Git. Keduanya valid — pilih yang paling cocok dengan gaya kerja kalian.

Pendekatan 1: Git Bare Repository (Trick git init --bare)

Ide dasarnya: buat satu repository Git "telanjang" (tanpa working tree) yang menggunakan $HOME sebagai working tree. Dengan cara ini, ~/.config/nvim/ bisa di-tracking tanpa harus memindahkan file-file-nya — mereka tetap di lokasi aslinya, dan Git hanya mencatatnya.

Inisialisasi bare repo untuk dotfiles
git init --bare $HOME/.dotfiles
alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles --work-tree=$HOME'
dotfiles config status.showUntrackedFiles no
dotfiles config core.excludesFile '~/.gitignore_global'
 
# Mulai tracking config Neovim
dotfiles add ~/.config/nvim
dotfiles commit -m "feat: add neovim config"

Important

Perhatikan baris dotfiles config status.showUntrackedFiles no. Tanpa baris ini, dotfiles status akan mencoba menampilkan seluruh isi $HOME sebagai file untracked — itu sangat berisik dan berbahaya, karena $HOME berisi segalanya. Selalu set opsi ini sebelum repo dipakai.

Supaya alias dotfiles tidak hilang setiap ganti shell, simpan di file rc kalian (~/.zshrc, ~/.bashrc, atau ~/.config/fish/config.fish):

Definisikan alias dotfiles di shell rc
alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles --work-tree=$HOME'

Keunggulan pendekatan ini: file-file dotfiles tetap di lokasi aslinya — tidak ada symlink yang bisa putus, dan semua aplikasi membaca config dari path standar. Ini pendekatan yang dipakai banyak engineer karena paling dekat dengan "bagaimana Git bekerja" — hanya saja working tree-nya $HOME.

Pendekatan kedua: simpan seluruh dotfiles di satu direktori (misalnya ~/dotfiles/), lalu buat symlink ke lokasi-lokasi yang dibutuhkan.

Setup symlink dotfiles
mkdir -p ~/dotfiles
git clone git@github.com:username/dotfiles.git ~/dotfiles
 
# Simetaskan config Neovim
ln -sf ~/dotfiles/nvim ~/.config/nvim

Struktur direktori ~/dotfiles/ bisa terlihat seperti ini:

Linux~/dotfiles/ — struktur
dotfiles/
├── nvim/
│   ├── init.lua
│   └── lua/
├── zsh/.zshrc
├── git/.gitconfig
├── tmux/.tmux.conf
├── install.sh
└── README.md

Keunggulannya: semua dotfiles terkumpul rapi dalam satu direktori sehingga mudah di-eksplorasi, di-backup, dan di-clone. Kekurangannya: symlink bisa putus jika struktur direktori berubah, dan kalian harus ingat membuat symlink baru setiap menambah satu dotfile baru.

Dotfiles Privat vs Publik

Satu pertanyaan yang sering muncul: dotfiles itu publik atau privat? Jawaban jujurnya: tergantung isinya. Config Neovim pada umumnya aman untuk publik — justru banyak engineer terkenal yang mempublikasikan dotfiles-nya sebagai referensi belajar. Tapi ada beberapa hal yang tidak boleh pernah masuk ke repository publik:

  • Token, API key, atau credential (~/.config/gh/hosts.yml, kredensial cloud).
  • File .env yang berisi secret.
  • Konfigurasi internal perusahaan (VPN config, hostname internal).
  • Riwayat shell yang sensitif (~/.zsh_history).

Warning

Pola aman yang umum: satu repository publik untuk config yang ingin dibagikan, dan satu repository privat untuk yang sensitif — atau gunakan satu repository privat dan .env.local yang di-gitignore untuk secret. Di Neovim, pola ini direpresentasikan oleh file seperti .env.local yang di-load hanya jika ada, dan ditambahkan ke .gitignore. Jangan pernah menaruh secret langsung di init.lua.

Contoh: config Neovim yang membutuhkan token AI (misalnya untuk Copilot atau provider LLM). Jangan tulis tokennya langsung — baca dari environment variable atau file yang di-ignore:

Baca secret dari environment, bukan hardcode
-- Token dibaca dari environment, tidak pernah di-commit
local api_key = vim.env.CODIUM_API_KEY
if not api_key then
  -- fallback: file .env.local yang di-gitignore
  local env_file = vim.fn.stdpath("config") .. "/.env.local"
  if vim.fn.filereadable(env_file) == 1 then
    for line in io.lines(env_file) do
      local k, v = line:match("^([^=]+)=(.*)$")
      vim.env[k] = v
    end
  end
end

Dan pastikan di .gitignore:

.gitignore — dotfiles
.env.local
.env
*.token
secrets/

Portabilitas Lintas Platform

Config yang hanya jalan di Linux adalah bom waktu. Banyak engineer bekerja di mesin kantor (Windows/Linux), laptop pribadi (macOS), dan server produksi (Linux) — dengan satu config yang sama. Kuncinya adalah mendeteksi OS secara dinamis, bukan meng-hardcode path.

Ada dua API yang paling umum dipakai: vim.loop.os_uname().sysname (nilai: Linux, Darwin, Windows_NT) dan jit.os (nilai: Linux, OSX, Windows). Kita bisa membuat module kecil yang menjadi "single source of truth" untuk deteksi OS:

lua/config/env.lua — deteksi OS & path lintas platform
local M = {}
 
local sysname = vim.loop.os_uname().sysname
 
M.is_windows = sysname == "Windows_NT"
M.is_mac     = sysname == "Darwin"
M.is_linux   = sysname == "Linux"
 
-- stdpath sudah menangani perbedaan lokasi config/data antar OS
M.config_dir = vim.fn.stdpath("config")
M.data_dir   = vim.fn.stdpath("data")
M.state_dir  = vim.fn.stdpath("state")
 
-- Lokasi yang sama antar OS tapi disimpan agar mudah di-override
M.undo_dir     = M.data_dir .. "/undo"
M.session_dir  = M.data_dir .. "/sessions"
M.autoload_dir = M.data_dir .. "/autoload"
 
return M

Note

vim.fn.stdpath("config") dan vim.fn.stdpath("data") adalah kunci portabilitas di Neovim. Di Linux keduanya adalah ~/.config/nvim/ dan ~/.local/share/nvim/, di macOS sama dengan Linux, dan di Windows otomatis mengarah ke %LOCALAPPDATA%\nvim\ dan %LOCALAPPDATA%\nvim-data\. Dengan memakai API ini, kalian tidak perlu menulis path absolut yang berbeda-beda per OS.

Conditional Per-OS di init.lua

Setelah module env.lua siap, gunakan di seluruh config untuk percabangan perilaku. Ada tiga level tempat conditional ini biasa dipakai:

  1. Opsi editor — misalnya path undodir dan session yang perlu dibuat dulu (mkdir -p) karena di Windows lokasinya berbeda.
  2. Keymaps — shortcut yang melibatkan modifier berbeda per OS.
  3. Plugin & build — beberapa plugin butuh dependency yang berbeda per OS.
lua/config/options.lua — opsi portabel
local env = require("config.env")
 
vim.opt.undofile = true
vim.opt.undodir = env.undo_dir
vim.opt.sessionoptions = "blank,buffers,curdir,folds,help,tabpages,winsize,winpos,terminal,localoptions"
 
-- Pastikan direktori undo & sessions ada di semua OS
for _, dir in ipairs({ env.undo_dir, env.session_dir }) do
  vim.fn.mkdir(dir, "p")
end
 
-- Clipboard: Windows & macOS butuh perlakuan khusus
if env.is_windows then
  vim.opt.clipboard = ""          -- gunakan win32yank/win32yank.exe via plugin
else
  vim.opt.clipboard = "unnamedplus"
end

Contoh keymap yang berbeda per OS — di macOS, Ctrl banyak dipakai sistem, sehingga sebagian orang menukar leader:

lua/config/keymaps.lua — leader per OS
local env = require("config.env")
 
-- Di macOS, banyak yang memakai Leader = "," atau tetap " " (spasi)
if env.is_mac then
  vim.g.mapleader = ","
else
  vim.g.mapleader = " "
end

Terakhir, contoh conditional pada spec plugin — ada plugin yang butuh binary eksternal dengan nama berbeda per OS (misalnya rg.exe vs rg), atau build command yang berbeda:

lua/plugins/telescope.lua — build spesifik OS
local env = require("config.env")
 
{
  "nvim-telescope/telescope.nvim",
  dependencies = {
    -- fzf-native adalah C-extension; build-nya sama di semua OS, tapi
    -- membutuhkan toolchain C. Di Windows, pastikan ada `make` (e.g. via MSYS2)
    { "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
  },
  keys = {
    { "<leader>ff", "<cmd>Telescope find_files<CR>", desc = "Find Files" },
    { "<leader>fg", "<cmd>Telescope live_grep<CR>", desc = "Live Grep" },
  },
  config = function()
    -- rg (ripgrep) wajib ada di semua OS; path-nya dipegang Telescope sendiri
    require("telescope").setup({
      defaults = {
        file_ignore_patterns = { "node_modules", ".git", "dist", "build" },
      },
    })
  end,
}

Tip

Prinsip DRY (Don't Repeat Yourself) juga berlaku di dotfiles: semua path dan flag yang sensitif terhadap OS dipusatkan di satu module env.lua, lalu module-module lain tinggal require. Jika nanti kalian pindah ke Windows, cukup perbaiki satu file, bukan sepuluh.

Bootstrap Otomatis di Mesin Baru

Setelah dotfiles ada di Git, langkah terakhir adalah bootstrap script: satu perintah yang menginstall Neovim beserta semua dependency-nya di mesin baru. Ini adalah script yang kalian jalankan pertama kali setelah clone. Prinsipnya sama seperti provisioning server: idempotent, cepat, dan aman dijalankan berulang.

install.sh — bootstrap dotfiles
#!/usr/bin/env bash
set -euo pipefail
 
# 1. Deteksi OS & pilih package manager
if [[ "$OSTYPE" == "darwin"* ]]; then
  install_cmd="brew install"
  deps=(neovim git ripgrep fd lazygit)
elif [[ -f /etc/debian_version ]]; then
  sudo apt-get update -y
  sudo apt-get install -y neovim git ripgrep fd-find
  install_cmd="sudo apt-get install -y"
  deps=()
elif [[ "$OSTYPE" == "msys" ]] || grep -qi microsoft /proc/version 2>/dev/null; then
  echo "Gunakan winget/scoop di Windows, atau jalankan ini di WSL." >&2
  exit 1
fi
 
for pkg in "${deps[@]}"; do
  $install_cmd "$pkg"
done
 
# 2. Setup bare repo dotfiles (sesuaikan dengan remote kalian)
git clone --bare https://github.com/username/dotfiles.git "$HOME/.dotfiles"
alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles --work-tree=$HOME'
dotfiles config status.showUntrackedFiles no
dotfiles checkout
dotfiles submodule update --init --recursive
 
# 3. Install plugin Neovim secara headless (tanpa UI)
nvim --headless "+Lazy! sync" +qa
nvim --headless "+MasonInstallAll" +qa 2>/dev/null || true
 
# 4. Verifikasi
nvim --headless -c 'lua print("dotfiles ready ✔")' -c 'qa'
echo "Setup selesai. Buka Neovim dan nikmati config kalian!"
Satu perintah saja untuk semuanya
curl -fsSL https://raw.githubusercontent.com/username/dotfiles/main/install.sh | bash

Banyak yang menyebut pola ini "provisioning as code untuk workstation". Anggap saja seperti terraform apply untuk laptop kalian — satu sumber kebenaran, satu perintah, hasil yang dapat diprediksi. Ketika kalian berganti laptop atau menambahkan server baru, tidak ada lagi pertanyaan "plugin apa yang harus saya install?"

Caution

Peringatan keamanan: menjalankan curl | bash dari internet adalah praktik yang rawan jika tidak hati-hati. Pastikan kalian meninjau isi script sebelum menjalankannya (atau setidaknya memastikan repository milik kalian sendiri dan branch yang benar). Untuk environment perusahaan, lebih aman men-download script, review, lalu eksekusi secara terpisah.

Kesalahan Umum (Common Pitfalls)

  1. Secret bocor ke repository. Paling berbahaya dan paling sering terjadi. Solusi: gunakan .gitignore, baca secret dari environment atau file .env.local, dan sebelum push pertama, periksa dotfiles status dengan teliti. Kalau terlanjur bocor, rotasi secret itu segera — Git history tidak akan memaafkan.

  2. Hardcode path absolut. Menulis /home/arman/.local/share/nvim/... akan rusak di mesin lain. Gunakan vim.fn.stdpath(), vim.fn.expand("~"), atau module env.lua.

  3. Plugin path berbeda per OS. Beberapa plugin memerlukan binary eksternal yang lokasinya berbeda (misalnya fd vs fdfind di Debian, atau make yang tidak ada secara default di Windows). Deteksi OS dan dokumentasikan dependency di install.sh.

  4. init.lua yang menolak jalan di Windows. Perhatikan perbedaan: path separator (\ vs /), shell default (pwsh vs bash), dan vim.opt.clipboard yang perlu plugin tambahan (win32yank). Satu kondisi if env.is_windows di awal config akan menyelamatkan banyak debugging.

  5. Symlink yang putus. Jika memakai pendekatan symlink dan kemudian merestrukturisasi ~/dotfiles/, semua symlink yang menunjuk ke path lama akan patah. Gunakan satu lokasi yang stabil, dan verifikasi dengan ls -l setelah setup.

  6. Lupa meng-commit perubahan. Dotfiles hanya berguna jika selalu sinkron. Jadikan kebiasaan: setelah menyetel config sampai nyaman, langsung dotfiles add -A && dotfiles commit. Semakin sering commit, semakin aman.

Checklist Portabilitas

Sebelum menganggap config kalian "siap lintas mesin", lewati checklist ini:

NoItemStatus
1Config tersimpan di repository Git (bare atau symlink)
2Tidak ada secret/token di repository
3Semua path memakai stdpath / $HOME / module env.lua
4Deteksi OS (Linux/macOS/Windows) terpusat di satu module
5Directory undodir, session dibuat otomatis (mkdir -p)
6Bootstrap script idempotent & sudah teruji di 2+ mesin
7Dependency eksternal terdokumentasi di install.sh / README

Penutup

Pada episode 24 ini kita telah belajar bahwa config Neovim kalian adalah aset yang harus dikelola secara profesional: menyimpannya ke Git melalui pendekatan bare repository atau symlink, memisahkan yang privat dari yang publik, membuatnya portabel lintas platform dengan module env.lua dan vim.fn.stdpath(), serta mengotomatiskan seluruh setup dengan bootstrap script. Mulai sekarang, pindah ke mesin baru bukan lagi mimpi buruk — cukup clone, bootstrap, dan selesai.

Poin kuncinya: dotfiles adalah investasi. Waktu yang kalian habiskan untuk merapikannya hari ini akan dibayar kembali berkali-kali lipat setiap kali kalian berpindah mesin, dan sekaligus menjadi portofolio yang menunjukkan bagaimana kalian bekerja sebagai engineer.

Di episode 25 selanjutnya, kita akan membahas pertanyaan yang sering menghantui setiap pengguna Neovim: haruskah saya membangun config sendiri dari nol, atau cukup memakai distro seperti LazyVim, NvChad, atau AstroNvim? Kita akan membandingkan kelebihan dan kekurangan masing-masing, kapan harus memilih yang mana, dan bagaimana menjadikan Neovim sebagai daily driver yang sesungguhnya. Sampai jumpa!

Belajar Neovim - Mengelola Dotfiles & Sharing Config Across Machines | Belajar Neovim