119 lines
5.8 KiB
Markdown
119 lines
5.8 KiB
Markdown
---
|
|
title: "Remote Server Dotfiles Bootstrap — Full Stack via SSH Script"
|
|
aliases: [remote-dotfiles, ssh-bootstrap, server-setup-script, dotfiles-remote]
|
|
tags: [dotfiles, fish, ssh, sshpass, neovim, starship, homelab, bash]
|
|
sources:
|
|
- "daily/2026-04-19.md"
|
|
created: 2026-04-19
|
|
updated: 2026-04-19
|
|
---
|
|
|
|
# Remote Server Dotfiles Bootstrap — Full Stack via SSH Script
|
|
|
|
When setting up a new remote server to match a local development environment, a single bash script using `sshpass` can install the complete stack (Fish, Starship, Neovim, CLI tools, fnm) and apply existing configs — without ever manually SSHing in. The pattern uses a helper function `r()` that wraps each `ssh` call, making the script readable and idempotent.
|
|
|
|
## Key Points
|
|
|
|
- Use `sshpass -p "$PASS" ssh $SSH_OPTS "$SERVER" "$@"` as a helper function `r()` — each command is a one-liner that looks like a local command
|
|
- **Idempotency:** every tool install wraps with `command -v tool || install_command` — safe to re-run
|
|
- **Fish conf.d/ pattern:** create separate init files in `~/.config/fish/conf.d/` for PATH, Starship, zoxide, fnm — cleaner than one monolithic `config.fish`
|
|
- Neovim as AppImage avoids version conflicts with distro packages
|
|
- `sshpass` must be installed on the local machine (not the remote): `brew install sshpass` (macOS) or `apt install sshpass` (Linux)
|
|
|
|
## Details
|
|
|
|
### The r() Helper Pattern
|
|
|
|
```bash
|
|
SERVER="vs@192.168.1.39"
|
|
PASS="yourpassword"
|
|
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=30 -o ServerAliveInterval=60"
|
|
r() { sshpass -p "$PASS" ssh $SSH_OPTS "$SERVER" "$@"; }
|
|
|
|
# Usage — clean, readable
|
|
r "apt-get update -qq"
|
|
r "command -v fish || apt-get install -y fish"
|
|
```
|
|
|
|
`-o StrictHostKeyChecking=no` skips the fingerprint confirmation for new hosts. `ServerAliveInterval=60` prevents connection drops during long-running installs.
|
|
|
|
### Full Stack Installation
|
|
|
|
**System packages first:**
|
|
```bash
|
|
r "echo '$PASS' | sudo -S apt-get install -y git curl wget build-essential tmux unzip python3 python3-pip fontconfig"
|
|
```
|
|
|
|
**Fish shell:**
|
|
```bash
|
|
r "command -v fish || (echo '$PASS' | sudo -S apt-add-repository -y ppa:fish-shell/release-3 && echo '$PASS' | sudo -S apt-get install -y fish)"
|
|
```
|
|
|
|
**Starship prompt:**
|
|
```bash
|
|
r "command -v starship || curl -sS https://starship.rs/install.sh | sh -s -- --yes"
|
|
```
|
|
|
|
**Neovim AppImage** (avoids old distro versions):
|
|
```bash
|
|
r "command -v nvim || (mkdir -p ~/.local/bin && curl -fLo ~/.local/bin/nvim https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.appimage && chmod +x ~/.local/bin/nvim)"
|
|
```
|
|
|
|
**Rust CLI tools:**
|
|
```bash
|
|
r "echo '$PASS' | sudo -S apt-get install -y bat fzf ripgrep fd-find"
|
|
# eza (apt version may be outdated — install from GitHub release)
|
|
r "command -v eza || (EZA=\$(curl -s https://api.github.com/repos/eza-community/eza/releases/latest | grep tag_name | cut -d'\"' -f4) && curl -fLo /tmp/eza.tar.gz https://github.com/eza-community/eza/releases/download/\${EZA}/eza_x86_64-unknown-linux-musl.tar.gz && tar -xzf /tmp/eza.tar.gz -C /tmp/ && sudo mv /tmp/eza /usr/local/bin/)"
|
|
r "command -v zoxide || curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash"
|
|
r "command -v lazygit || (LGV=\$(curl -s https://api.github.com/repos/jesseduffield/lazygit/releases/latest | grep tag_name | cut -d'\"' -f4 | sed 's/v//') && curl -fLo /tmp/lg.tar.gz https://github.com/jesseduffield/lazygit/releases/download/v\${LGV}/lazygit_\${LGV}_Linux_x86_64.tar.gz && tar -xzf /tmp/lg.tar.gz -C /tmp/ lazygit && sudo mv /tmp/lazygit /usr/local/bin/)"
|
|
```
|
|
|
|
**Node.js via fnm:**
|
|
```bash
|
|
r "command -v fnm || curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-dir '\$HOME/.local/share/fnm' --skip-shell"
|
|
r "export PATH=\"\$HOME/.local/share/fnm:\$PATH\" && fnm install --lts && fnm default lts-latest"
|
|
```
|
|
|
|
### Fish conf.d/ Init Files
|
|
|
|
Instead of one big `config.fish`, create small init files:
|
|
|
|
```bash
|
|
r "mkdir -p ~/.config/fish/conf.d"
|
|
r "printf 'fish_add_path \$HOME/.local/bin\nfish_add_path \$HOME/.cargo/bin\nfish_add_path \$HOME/.local/share/fnm\n' > ~/.config/fish/conf.d/00-paths.fish"
|
|
r "printf 'if command -q starship; starship init fish | source; end\n' > ~/.config/fish/conf.d/01-starship.fish"
|
|
r "printf 'if command -q zoxide; zoxide init fish | source; end\n' > ~/.config/fish/conf.d/02-zoxide.fish"
|
|
r "printf 'if command -q fnm; fnm env --use-on-cdpath | source; end\n' > ~/.config/fish/conf.d/03-fnm.fish"
|
|
```
|
|
|
|
This pattern keeps each concern isolated and makes it easy to disable specific components.
|
|
|
|
### Set Fish as Default Shell
|
|
|
|
```bash
|
|
r "FISH=\$(which fish) && grep -qF \"\$FISH\" /etc/shells || echo \"\$FISH\" | sudo tee -a /etc/shells"
|
|
r "sudo chsh -s \$(which fish) vs"
|
|
```
|
|
|
|
### Verification
|
|
|
|
```bash
|
|
r "export PATH=\"\$HOME/.local/bin:\$HOME/.local/share/fnm:\$PATH\"
|
|
for t in fish starship nvim tmux fzf bat rg eza zoxide lazygit; do
|
|
command -v \$t &>/dev/null && echo \"✓ \$t\" || echo \"✗ \$t\"
|
|
done"
|
|
```
|
|
|
|
### Assumption: Configs Already Copied
|
|
|
|
This script assumes dotfiles were already pushed to the server (e.g., via rsync or direct copy from Nextcloud/iCloud sync). The install script only installs tools and creates conf.d init files — it does not touch existing `~/.config/fish/`, `~/.config/nvim/`, or `~/.config/starship.toml`. Those files are expected to be present already.
|
|
|
|
## Related Concepts
|
|
|
|
- [[wiki/concepts/fish-abbr-patterns]] — Fish abbreviations configured after this bootstrap
|
|
- [[wiki/concepts/fish-shell-path-config]] — Fish PATH management patterns used in conf.d files
|
|
- [[wiki/dotfiles/_index]] — full dotfiles topic: Kitty, Fish, WezTerm, LazyVim, Rust CLI tools
|
|
|
|
## Sources
|
|
|
|
- [[daily/2026-04-19.md]] — Remote server setup for `vs@192.168.1.39`; full bootstrap script generated with `r()` helper pattern; Fish + Starship + Neovim + Rust CLI tools + fnm; Fish set as default shell; conf.d/ pattern used for initialization
|