| config/zsh | ||
| lib | ||
| modules | ||
| .gitignore | ||
| install.sh | ||
| README.md | ||
| setup.sh | ||
bootstrap
Turn a fresh Debian/Ubuntu/Arch VPS into a familiar workstation in one command. Detects the distro, installs a curated CLI package set, sets up zsh + oh-my-zsh with your ported config, brings your nvim config, hardens sane system defaults, and reports state.
Usage
curl -fsSL <shortener> | bash
# run everything non-interactively:
curl -fsSL <shortener> | bash -s -- --yes
# run a single step:
curl -fsSL <shortener> | bash -s -- --only packages
The shortener points at the raw install.sh in this repo, which clones the
full repo to ~/.bootstrap and runs setup.sh. To update: cd ~/.bootstrap && git pull (or just re-run the one-liner — it pulls existing clones).
Re-run individual steps locally after the initial bootstrap:
bash ~/.bootstrap/setup.sh --only shell-zsh
bash ~/.bootstrap/setup.sh --only hardening
Steps
| # | step | what it does |
|---|---|---|
| 0 | preflight | verify distro + network + privileges |
| 1 | packages | git, byobu, ufw, mosh, btop, eza, fzf, ripgrep, bat, zoxide, jq, fd, tree, ncdu, lazygit, starship, … (+ yay on Arch) |
| 2 | docker | Docker engine + compose v2 plugin, $USER in docker group |
| 3 | angie | Angie (nginx fork) web server |
| 4 | shell-zsh | zsh + oh-my-zsh + plugins, set default shell, ship config |
| 5 | nvm | nvm → ~/.nvm + Node LTS |
| 6 | nvim | clone g.mk.fo/bootstrap/nvim-minimal → ~/.config/nvim |
| 7 | hardening | ssh (key-only root), ufw, updates, unattended-upgrades, tz, hostname, swapfile, locale, fail2ban |
| 8 | sanity | read-only report + recommended manual follow-ups |
Every step is idempotent — re-running is safe.
Zsh plugins enabled (oh-my-zsh)
git sudo command-not-found colored-man-pages extract copypath dirhistory
plus, cloned into $ZSH_CUSTOM: zsh-autosuggestions,
zsh-syntax-highlighting (loaded last), zsh-completions,
history-substring-search, fzf-tab. Prompt: starship.
Config layout (shipped to ~/.config/zsh)
config/zsh/
zshrc # main config (sources the pieces below)
path.zsh # PATH + env (XDG, EDITOR, LANG, nvm, pnpm)
aliases.zsh # ported from fish + curated defaults
aliases-extras.zsh # your personal additions (preserved across re-runs)
functions.zsh # mcd, conf, so, ssh-wrapper, dot-expansion
plugins.zsh # omz plugin list + load oh-my-zsh
~/.zshrc is a tiny shim that sets ZDOTDIR=~/.config/zsh and sources the
real config there — keeps omz "happy" while your config lives under XDG.
Hardening choices
- SSH:
PermitRootLogin prohibit-password+PasswordAuthentication no. Root key login stays available; password login is disabled. A backup is made andsshd -truns before reload. Always keep your current session open and test a new login before closing it. - ufw: deny incoming, allow
22/tcp,60000:61000/udp(mosh),80,443/tcp(angie). - updates + unattended-upgrades (Debian), system upgrade on Arch.
- timezone (default UTC), hostname (optional).
- swapfile 2G if RAM < 2G and no swap present.
- locale en_US.UTF-8.
- fail2ban with an sshd jail.
Supported hosts
- Debian / Ubuntu (via apt)
- Arch (via pacman + yay for AUR)
- Architectures:
amd64,arm64
Notes
aliases-extras.zshis seeded once and then left alone — re-running the shell-zsh step won't clobber your personal aliases.- On Debian,
fd/batship asfdfind/batcat; symlinks to the canonical names are created in~/.local/bin(which is onPATHviapath.zsh). - Some Go/Rust CLIs (
dust,duf,procs,lazygit) are fetched from the latest GitHub release tarball when the distro package is missing; this is best-effort and warned (not fatal) on failure.