config
Personal dotfiles — vim + bash configuration and a one-shot installer.
Quick start
Install everything (clone + setup) with one command:
curl -fsSL https://git.bchanot.fr/bchanot/config/raw/branch/master/remote-install.sh | bash
(Runs a remote script through bash — see the Install section for what it does and the manual alternative.)
What's inside
| Path | Purpose |
|---|---|
install.sh |
Linux: installs apt packages + Docker + code-server + RDP (gnome-remote-desktop), backs up old config, deploys vim + bashrc (OS-detected), installs CLI scripts, pipx tools, a low-disk login warning and the cloudpex NAS mount helper; ends by offering two system changes (/tmp on disk, SSH memory guard). macOS: same tooling through Homebrew (see macOS), then prints what was not installed compared with Linux. |
cloudpex/ |
On-demand SMB mount of a NAS share (cloudpex command + its installer). Site values (host, share, SMB user, mount point, SMB version) are prompted at install and stored in /etc/cloudpex.conf, never in the script. French README inside. |
etc/tmpfiles.d/tmp.conf |
Cleanup rules for a disk-backed /tmp (wiped at boot, 10-day purge). Deployed by the /tmp on disk offer. |
etc/systemd/ssh.service.d/override.conf |
ssh.service drop-in: sshd exempt from the OOM killer + memory reclaim protection. Deployed by the SSH memory guard offer. |
etc/default/earlyoom |
earlyoom arguments: spare sshd/systemd, kill node/java first. Deployed by the SSH memory guard offer. |
etc/fail2ban/jail.d/local.conf |
fail2ban sshd jail: journal backend, all-ports ban, 5 tries / 10 min / 1 h, private LAN never banned. Deployed on every Linux install. |
etc/apt/apt.conf.d/20auto-upgrades |
Enables unattended security upgrades (what dpkg-reconfigure writes). Deployed on every Linux install. |
etc/ssh/sshd_config.d/20-hardening.conf |
sshd limits that cannot lock you out: PermitRootLogin no, MaxAuthTries 3, LoginGraceTime 20. Deployed on every Linux install after sshd -t. |
vim/vimrc |
Vim config: pathogen, molokai, syntastic (C with -Wall -Werror -Wextra), NERDTree, 42-style canonical class generators (:ClassH, :ClassC). |
vim/autoload/ |
pathogen.vim plugin loader (committed). |
vim/colors/ |
molokai.vim colorscheme (committed). |
bash/bashrc-linux |
bashrc for desktop Linux (git-aware prompt + command timer). |
bash/bashrc-osx |
bashrc for macOS: bashrc-linux adapted (Homebrew on PATH, BSD ls -G, bash 5 clock for the timer, cc without systemd-run). |
zsh/zshrc-osx |
zshrc for macOS when zsh is chosen: oh-my-zsh + the same env, aliases and dtach menu as bashrc-osx. Loads ~/.zshrc.local for machine-specific lines. |
zsh/bchanot.zsh-theme |
oh-my-zsh theme reproducing the bash prompt: ✔ (12ms) user [ ~/dir ] [branch -*+] >. |
bin/dt |
dtach session manager for claude-in-dtach sessions. |
bin/dtach-router |
Dashboard to resume dtach sessions, shown at the start of every interactive shell (wired into ~/.bashrc by the installer). |
bin/claude-provider |
Switch Claude Code between Anthropic and OpenRouter. |
etc/profile.d/disk-usage-warning.sh |
Login-time warning (bold red) when / or /home cross 85% usage. Deployed to /etc/profile.d/ on Linux. |
Install
One-liner (clone + install)
curl -fsSL https://git.bchanot.fr/bchanot/config/raw/branch/master/remote-install.sh | bash
remote-install.sh ensures git is present, clones the repo to ~/config (or pulls if already there), then runs install.sh. Override with env vars: REPO_URL=... CLONE_DIR=... BRANCH=... curl ... | bash.
Piping a remote script into
bashruns unreviewed code over the network. Readremote-install.shfirst, or use the manual clone below.
Manual
git clone https://git.bchanot.fr/bchanot/config.git && cd config
./install.sh
No argument — the OS is auto-detected.
What it does:
- On Debian/Ubuntu, installs a set of CLI/dev packages via
apt-get(see below). On macOS, Homebrew does it instead: see macOS. - Sets up Docker's official apt repo (Ubuntu) and installs the engine + compose plugin — skipped if
dockeris already present. - Moves any existing
~/.vim,~/.vimrc,~/.bashrc,~/.Sublivimto~/Oldconfig. - Clones the
syntasticandnerdtreevim plugins into~/.vim/bundle/. - Copies the tracked vim files into
~/.vimand symlinks~/.vimrc. - Picks the bashrc by OS: macOS →
bashrc-osx(falls back tobashrc-linuxif missing), everything else →bashrc-linux. Copies it to~/.bashrc. - Installs Python CLIs via
pipx(PyMuPDF→pymupdf,Markdown→markdown_py) — skipped ifpipxis absent. - Copies the
bin/scripts (dt,dtach-router,claude-provider) into~/.local/bin. The dtach session-resume menu ships in the deployed bashrc (both OSes), so every interactive shell offers it — including VS Code Remote-SSH terminals, which are non-login and never read~/.profile. The installer also strips any older dtach block left in~/.profileso a plain SSH login doesn't prompt twice. - On Linux, installs
etc/profile.d/disk-usage-warning.shto/etc/profile.d/(needssudo) so each login warns when/or/homecross 85% usage. - On Linux, installs code-server (VS Code in the browser) via its vendor script — skipped if already present — and enables the
code-server@$USERsystemd service. - On Linux, installs
ubuntu-desktop-minimal(GDM + GNOME Shell, ~1.5 GB): the RDP remote login below hands out a GNOME session, which a bare server install does not have. Then sets up RDP remote login viagnome-remote-desktop(Wayland-native): installs the daemon +openssl, generates a self-signed TLS cert once, and prompts interactively for shared "gate" credentials (skipped when no terminal is attached, or already set). Disablesxrdpif present; opens UFW port3389only when UFW is already active. Finally, whenlspcisees an NVIDIA GPU, runsubuntu-drivers installto put on the driver the distro recommends for the card (no version pinned; loads at the next reboot). Skipped on machines without an NVIDIA GPU. - On Linux, installs the
cloudpexNAS mount helper to/usr/local/binviacloudpex/install.sh, which prompts for the NAS host, share name, SMB user, mount point and SMB version and writes them to/etc/cloudpex.conf(root,0600; an existing config is shown and kept unless you sayn; skipped when no terminal is attached). Nothing is mounted, no password stored, seecloudpex/README.md. - On Linux, installs the security baseline, always, no prompt: fail2ban (+
nftables) withetc/fail2ban/jail.d/local.conf(sshd jail reading the journal, bans the offending IP on every port so the SSH port does not matter, 5 failures in 10 min → 1 h ban, loopback and private LAN ranges never banned); unattended-upgrades enabled throughetc/apt/apt.conf.d/20auto-upgrades; and the sshd drop-inetc/ssh/sshd_config.d/20-hardening.conf(PermitRootLogin no,MaxAuthTries 3,LoginGraceTime 20), checked withsshd -tand removed again if sshd rejects it, thenreload ssh. Authentication methods, port and user lists are left as they are. - On Linux, at the very end, offers (
[y/N], skipped when no terminal is attached) to move/tmpto disk: Ubuntu mounts/tmpas a RAM-backed tmpfs capped at 50% of RAM, which agent runs fill, halving the RAM and breaking every shell with "No space left on device". Accepting maskstmp.mountand installsetc/tmpfiles.d/tmp.conf(wipe at boot, 10-day purge). Effective at the next reboot. - On Linux, at the very end, offers to keep SSH reachable under memory pressure: installs the
ssh.servicedrop-in (OOMScoreAdjust=-1000,MemoryMin=256M) andearlyoomwithetc/default/earlyoom(kills the largest process,node/javafirst and neversshd, once free RAM and swap both drop under 10%). Restartingsshkeeps open sessions. Note:MemoryMinprotects the sshd daemon only; login sessions live inuser.slice, so no setting can reserve RAM for a future shell. earlyoom acting in time is the real protection.
Packages installed (apt)
- Build / VCS / C dev:
vim git git-lfs git-filter-repo gitleaks gcc make pkg-config dkms valgrind shellcheck gh - Net / security / transport:
curl gnupg ca-certificates apt-transport-https net-tools openssh-server cifs-utils lftp ftp - Shell tooling:
unzip tree tmux fzf dtach - Runtimes:
nodejs python3-pip pipx php-cli - Web stack (local WordPress/LAMP):
mariadb-server imagemagick php-mysql php-gd php-imagick php-mbstring php-xml php-intl php-curl(unversionedphp-*metapackages, so they follow the distro's PHP) - Media / doc CLI:
ffmpeg weasyprint poppler-utils qpdf webp libavif-bin - Docker:
docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin(via Docker's repo) - Desktop / GPU:
ubuntu-desktop-minimal(always, Linux) + the distro-recommended NVIDIA driver viaubuntu-drivers install(only when an NVIDIA GPU is detected) - Remote access:
gnome-remote-desktop openssl(apt) +code-server(via its vendor install script, not apt) — RDP remote login + browser VS Code - pipx:
PyMuPDF(pymupdf),Markdown(markdown_py) - Security baseline (Linux, always):
fail2ban nftables unattended-upgrades - Optional (end-of-install offer, Linux):
earlyoom
The script is re-runnable: each run re-backs up to ~/Oldconfig (overwriting the previous backup), re-clones plugins, skips Docker if already installed, and re-deploys the bin/ scripts.
Note: the Docker repo step assumes Ubuntu.
macOS
The same ./install.sh detects macOS and replaces apt-get with Homebrew. It first asks which login shell you want, bash or zsh ([bash] by default; answer in advance with MACOS_SHELL=zsh ./install.sh, and with no terminal attached it picks bash):
- Installs Homebrew with its official script when
brewis missing (this also pulls the Xcode Command Line Tools: clang, make, git), thenbrew update+brew upgrade. - Installs the apt list mapped to formulae:
vim git git-lfs git-filter-repo gitleaks pkgconf shellcheck gh curl gnupg lftp inetutils unzip tree tmux fzf dtach node python pipx php mariadb imagemagick ffmpeg weasyprint poppler qpdf webp libavif bash. Brew'sphpalready ships gd, mbstring, xml, intl, curl and mysql. - Docker:
colima(the Linux VM) +docker docker-compose docker-buildx. Writes~/.docker/config.jsonwithcliPluginsExtraDirssodocker composeworks, only when that file does not exist yet (otherwise prints the line to add). - Starts
colima,code-serverandmariadbasbrew services(thesystemctl enable --nowequivalent), skipping any already started. - Deploys
bashrc-osx, then appends one line to~/.bash_profilethat sources~/.bashrc: macOS terminals open login shells, which never read~/.bashrcon their own. Done for both choices, sobashstays usable. - bash chosen: makes brew's bash 5 the login shell (adds it to
/etc/shellswithsudo, thenchsh, which asks for your password). macOS ships bash 3.2, too old for the bashrc. zsh chosen: installs oh-my-zsh with its official script (unattended, skipped if~/.oh-my-zshexists), deployszsh/zshrc-osxto~/.zshrcand thebchanottheme to~/.oh-my-zsh/custom/themes/, then makes/bin/zshthe login shell. An existing~/.zshrcthat differs from the repo's is saved as~/.zshrc.backup-<date>(outside~/Oldconfig, which every run wipes). Move your machine-specific lines (nvm, bun, tokens) into~/.zshrc.local: the deployed zshrc loads it. - Ends with the list of what the Linux install has and this one does not:
gcc(Apple clang answers togcc),valgrind,dkms,net-tools,openssh-serverand the RDP desktop (both built into macOS, switched on in System Settings > Sharing),cifs-utils,php-imagick, the NVIDIA driver, the disk-usage warning,cloudpex, the security baseline and the two end-of-install offers.
CLI scripts (bin/)
Deployed to ~/.local/bin (the deployed bashrc adds this dir to PATH):
dt— manage claude-in-dtach sessions (dt ls|at|kill). Needsdtach+fzf.dtach-router— session dashboard shown at shell startup. It ships in the deployed bashrc and is sourced (not executed) in every interactive shell, so it also fires in VS Code Remote-SSH terminals (non-login shells that skip~/.profile). Silent no-op when no session exists. Create a session withcc [name], re-open the menu anytime withd(both aliases from the bashrc). Needsdt,dtach,fzf.claude-provider— switch Claude Code between Anthropic and OpenRouter. OpenRouter mode reads the key from$OPENROUTER_API_KEY(never hardcoded). Export it from a private, untracked file, e.g.~/.bashrc.local:export OPENROUTER_API_KEY="<your-openrouter-key>"
Requirements
bash,git- Debian/Ubuntu
apt-get, or macOS (Homebrew is installed if missing) - A
bashlogin shell on Linux (zsh users switch to bash for these prompts to apply). On macOS the installer sets bash or zsh, your choice
License
GPL-3.0-or-later — see LICENSE.
Copyright (C) 2026 Bastien Chanot.