Masalah: Server Kamu Bukan Laptop Gaming
Cerita klasik: kamu spin up VPS 2GB RAM buat development, terus install VS Code Server atau Jupyter dengan tampilan lengkap. Belum nulis satu baris kode pun, RAM sudah kepake 1.4GB. Sisanya buat Node.js atau Python runtime? Mepet banget.
Itu bukan masalah spec — itu masalah arsitektur. Kamu masih mikir development environment = harus ada GUI. Padahal buat sebagian besar workflow backend, API development, sampai data pipeline, GUI itu overhead yang nggak perlu.
Headless development environment adalah setup di mana semua proses development — editing, running, debugging, testing — jalan tanpa graphical interface. Semua lewat terminal, SSH, atau API. Hasilnya: resource lebih efisien, reproducible di mesin mana pun, dan bisa di-automate dari awal sampai akhir.
Kenapa Headless Lebih Masuk Akal untuk Banyak Kasus
Sebelum gue jelasin setupnya, mari kita jujur soal trade-off.
Headless cocok kalau:
- Kamu kerja di remote server atau VPS dengan RAM terbatas
- Project kamu butuh environment yang reproducible (misalnya onboarding anggota tim baru)
- Kamu sering context-switch antara beberapa project dengan dependency berbeda
- CI/CD pipeline kamu harus mirror environment development lokal
Headless kurang cocok kalau:
- Kamu heavy user Figma, desain UI, atau butuh browser preview intensif
- Workflow kamu sangat bergantung pada GUI debugger (misal Android Studio)
Untuk backend developer, indie hacker yang deploy ke VPS, atau mahasiswa yang kerja di mesin shared — headless adalah pilihan yang solid.
Komponen Inti Headless Dev Environment
Setup headless yang proper punya empat lapisan:
- Shell environment — zsh/bash dengan config yang konsisten
- Terminal multiplexer — tmux atau screen, biar session nggak mati kalau koneksi putus
- CLI editor — Neovim atau Helix
- Environment isolation — Docker, devcontainer, atau virtual environment per bahasa
Gue bakal tunjukin cara nyambungin semuanya dengan satu bootstrap script yang bisa kamu jalanin di VPS baru atau WSL.
Setup Dasar: Bootstrap Script
Script ini diasumsikan jalan di Ubuntu 22.04 / Debian. Kalau kamu pakai distro lain, sesuaikan package manager-nya.
#!/usr/bin/env bash
# bootstrap-headless.sh
# Tested on Ubuntu 22.04 LTS
set -euo pipefail
echo "==> Update packages"
sudo apt-get update -qq
sudo apt-get install -y \
git curl wget unzip \
tmux zsh fzf ripgrep \
build-essential \
python3-pip python3-venv \
nodejs npm
echo "==> Install Neovim (latest stable)"
curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz
tar -xzf nvim-linux64.tar.gz
sudo mv nvim-linux64 /opt/nvim
sudo ln -sf /opt/nvim/bin/nvim /usr/local/bin/nvim
rm nvim-linux64.tar.gz
echo "==> Install Oh My Zsh (unattended)"
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
echo "==> Configure tmux"
cat > ~/.tmux.conf << 'EOF'
set -g default-terminal "screen-256color"
set -g history-limit 10000
set -g mouse on
bind r source-file ~/.tmux.conf \; display "Config reloaded"
# Split panes dengan keybind yang lebih intuitif
bind | split-window -h
bind - split-window -v
EOF
echo "==> Done. Jalankan: exec zsh"
Simpan sebagai bootstrap-headless.sh, lalu:
chmod +x bootstrap-headless.sh
./bootstrap-headless.sh
Gotcha pertama yang gue kena: Kalau kamu install Neovim dari apt langsung (apt install neovim), kamu dapat versi lama (0.6.x) yang nggak kompatibel sama banyak plugin modern. Selalu ambil binary dari GitHub releases.
Isolasi Environment dengan Docker Dev Container
Bagian yang paling sering dilewatin orang: isolation. Kalau kamu install semua dependency langsung di host, cepat atau lambat ada konflik versi. Project A butuh Node 18, Project B butuh Node 20 — ribet.
Solusinya: setiap project punya container sendiri, dan kamu attach editor ke container itu.
Buat file .devcontainer/devcontainer.json di root project:
{
"name": "my-api-project",
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bookworm",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"postCreateCommand": "npm install",
"remoteUser": "node",
"mounts": [
"source=${localWorkspaceFolder},target=/workspace,type=bind"
],
"workspaceFolder": "/workspace"
}
Lalu jalankan container-nya:
# Install devcontainer CLI
npm install -g @devcontainers/cli
# Build dan start container
devcontainer up --workspace-folder .
# Exec ke dalam container
devcontainer exec --workspace-folder . bash
Dari dalam container, kamu bisa pakai Neovim, jalankan test, debug — semua headless. Kalau kamu mau attach VS Code dari laptop ke container yang jalan di VPS, itu juga bisa lewat Remote SSH + Dev Containers extension. Tapi editor-nya tetap jalan di local, bukan di server.
Gotcha kedua: devcontainer up butuh Docker daemon jalan. Kalau kamu di VPS dan pakai user non-root, pastikan user kamu sudah masuk group docker:
sudo usermod -aG docker $USER
newgrp docker
Neovim sebagai IDE Headless
Ini bagian yang paling banyak ditanya: "emang bisa se-powerful VS Code?"
Jawaban jujur: bisa, tapi butuh konfigurasi. Gue pakai setup minimal yang cover kebutuhan sehari-hari: LSP, autocomplete, fuzzy finder, dan git integration.
Install plugin manager dulu:
# Install lazy.nvim
git clone --filter=blob:none \
https://github.com/folke/lazy.nvim.git \
--branch=stable \
~/.local/share/nvim/lazy/lazy.nvim
Buat config di ~/.config/nvim/init.lua:
-- ~/.config/nvim/init.lua
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({ "git", "clone", "--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", lazypath })
end
vim.opt.rtp:prepend(lazypath)
-- Basic settings
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.expandtab = true
vim.opt.termguicolors = true
-- Plugins
require("lazy").setup({
-- LSP
{ "neovim/nvim-lspconfig" },
{ "williamboman/mason.nvim", config = true },
{ "williamboman/mason-lspconfig.nvim" },
-- Autocomplete
{ "hrsh7th/nvim-cmp" },
{ "hrsh7th/cmp-nvim-lsp" },
-- Fuzzy finder
{ "nvim-telescope/telescope.nvim",
dependencies = { "nvim-lua/plenary.nvim" } },
-- File tree
{ "nvim-tree/nvim-tree.lua" },
-- Git
{ "lewis6991/gitsigns.nvim", config = true },
-- Theme
{ "folke/tokyonight.nvim", priority = 1000,
config = function() vim.cmd.colorscheme("tokyonight") end },
})
-- Setup LSP untuk TypeScript dan Python
require("mason").setup()
require("mason-lspconfig").setup({
ensure_installed = { "ts_ls", "pyright", "lua_ls" },
automatic_installation = true,
})
local lspconfig = require("lspconfig")
local capabilities = require("cmp_nvim_lsp").default_capabilities()
for _, server in ipairs({ "ts_ls", "pyright" }) do
lspconfig[server].setup({ capabilities = capabilities })
end
Buka Neovim, jalankan :Lazy sync untuk install semua plugin. Pertama kali bisa makan 2-3 menit.
Gotcha ketiga: ts_ls (dulu tsserver) butuh typescript package terinstall di project. Kalau kamu dapat error "Cannot find typescript", jalankan npm install typescript di project directory-nya.
Tmux: Jangan Pernah Kehilangan Session Lagi
Ini yang paling underrated. Kalau koneksi SSH kamu putus di tengah-tengah proses build atau test yang panjang — dengan tmux, proses itu tetap jalan.
Workflow dasar:
# Buat session baru dengan nama project
tmux new-session -s myproject
# Di dalam tmux:
# Ctrl+b | -> split vertical
# Ctrl+b - -> split horizontal
# Ctrl+b d -> detach (session tetap jalan)
# Reconnect ke session
tmux attach-session -t myproject
# List semua session
tmux ls
Gue biasanya punya layout standar per project: pane kiri untuk editor, pane kanan atas untuk server/watcher, pane kanan bawah untuk git dan command ad-hoc.
Automasi layout ini dengan script:
#!/usr/bin/env bash
# dev-session.sh — buat tmux session dengan layout standar
SESSION=${1:-"dev"}
PROJECT_DIR=${2:-$(pwd)}
tmux new-session -d -s "$SESSION" -c "$PROJECT_DIR"
# Window 1: Editor
tmux rename-window -t "$SESSION:1" "editor"
tmux send-keys -t "$SESSION:editor" "nvim ." Enter
# Window 2: Terminal split
tmux new-window -t "$SESSION" -n "terminal" -c "$PROJECT_DIR"
tmux split-window -t "$SESSION:terminal" -h -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION:terminal.left" "npm run dev" Enter
# Attach
tmux attach-session -t "$SESSION"
Jalankan dengan:
chmod +x dev-session.sh
./dev-session.sh myapi ~/projects/my-api
Integrasi dengan CI/CD
Salah satu keuntungan headless development environment yang sering dilupain: environment kamu bisa di-mirror persis ke CI/CD pipeline.
Kalau kamu pakai GitHub Actions, tambahkan file .github/workflows/ci.yml:
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-22.04
container:
image: mcr.microsoft.com/devcontainers/typescript-node:1-20-bookworm
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Type check
run: npx tsc --noEmit
Image yang sama dengan devcontainer lokal kamu. Kalau test pass di lokal, harusnya pass di CI juga. Ini yang bikin "works on my machine" jadi masalah masa lalu.
Yang Gue Lakuin Sekarang
Setup headless development environment gue saat ini jalan di VPS 4GB RAM (sekitar $12/bulan). Di dalamnya ada:
- tmux dengan 4-5 session aktif untuk project yang berbeda
- Neovim dengan LSP untuk TypeScript, Python, dan Go
- Docker untuk isolasi per project
- devcontainer CLI untuk onboarding project baru dalam hitungan menit
RAM usage idle: sekitar 400MB. Bandingkan dengan setup VS Code Server yang bisa makan 1.2GB sebelum project dibuka.
Langkah lanjutan yang bisa kamu coba:
- Jalankan bootstrap script di VPS atau WSL kamu hari ini
- Pindahkan satu project ke devcontainer setup
- Biasakan tmux — minimal
new-session,attach,detachdulu - Kalau sudah nyaman, eksplorasi Neovim config lebih dalam — ada banyak starter config seperti LazyVim atau NvChad kalau mau yang lebih plug-and-play
Headless development environment bukan tentang jadi "power user" atau flex di Twitter. Ini tentang punya setup yang efisien, reproducible, dan nggak bikin kamu bergantung pada satu mesin atau satu GUI tertentu.