@sebastienrousseau/dotfiles
Version:
The Trusted Shell Platform — Universal dotfiles managed by Chezmoi. Features Bash & Zsh for macOS, Linux & WSL. Rust modern tooling & enterprise-grade security.
284 lines (198 loc) • 6.85 kB
Markdown
---
render_with_liquid: false
---
# Tutorial: Deploy to a Fleet
Roll out `.dotfiles` across multiple workstations and verify they're in sync.
## Scenario
You have three hosts:
- **macbook-t2** — your MacBook (current host, already installed)
- **surface-pro** — your Linux laptop (new install)
- **geekom-a9** — your desktop NUC (new install)
Goal: all three running identical `.dotfiles` with per-host customization, with auditable cross-host attestation.
## Step 1: Per-Host Install
On each new host:
```sh
bash -c "$(curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/dotfiles/master/install.sh)"
```
During `chezmoi init`, answer with the host's preset:
- Surface Pro → `machine = "surface-pro"`
- Geekom A9 → `machine = "geekom-a9"`
## Step 2: Exchange Signing Keys
Each host has its own SSH ED25519 key. To verify commits and attestations across the fleet, publish each host's public key to every other host.
**On each host, copy the public key:**
```sh
cat ~/.ssh/id_ed25519.pub
# ssh-ed25519 AAAAC3N...jkl5 user@macbook-t2
```
**Collect all three keys. Append them to `~/.ssh/allowed_signers` on every host:**
```
user@macbook-t2 ssh-ed25519 AAAAC3N...jkl5
user@surface-pro ssh-ed25519 AAAAC3N...abc7
user@geekom-a9 ssh-ed25519 AAAAC3N...def9
```
Optionally commit `allowed_signers` as a template so all hosts receive updates via `dot update`:
```sh
# On macbook-t2
chezmoi add ~/.ssh/allowed_signers
git add -A && git commit -sS -m "feat(ssh): add fleet signing keys"
git push
```
Now every other host gets the updated list on `dot update`.
## Step 3: Share the Age Key (for Shared Secrets)
If you want shared secrets decrypted on all three hosts, each host must have an Age key recipient in `.sops.yaml`.
Option A: Share one key across all hosts (simpler, less granular).
Option B: Each host has its own key; all three are listed as recipients (better operational security).
For Option B:
```sh
# On macbook-t2 (already set up)
grep "public key:" ~/.config/age/keys.txt
# On surface-pro
age-keygen -o ~/.config/age/keys.txt
grep "public key:" ~/.config/age/keys.txt
# On geekom-a9
age-keygen -o ~/.config/age/keys.txt
grep "public key:" ~/.config/age/keys.txt
```
Add all three public keys to `.sops.yaml`:
```yaml
creation_rules:
- path_regex: \.sops\.yaml$
age: >-
age1aaa...111, # macbook-t2
age1bbb...222, # surface-pro
age1ccc...333 # geekom-a9
```
Re-encrypt existing secrets for the new recipients:
```sh
# On macbook-t2
find . -name '*.sops.yaml' -exec sops updatekeys {} \;
git add .sops.yaml dot_config/*.sops.yaml
git commit -sS -m "chore(secrets): add fleet recipients"
git push
```
Other hosts on `dot update` decrypt with their own private key automatically.
## Step 4: Verify Fleet Alignment
From any host:
```sh
dot fleet attest
```
Expected output:
```
Fleet Attestation — v0.2.501
Host Git SHA Policy Tools OK Drift Verified
-------------- ---------- --------- ---------- ------- --------
macbook-t2 abc123d 0x7f2a… ✓ 0 ✓
surface-pro abc123d 0x7f2a… ✓ 0 ✓
geekom-a9 abc123d 0x7f2a… ✓ 0 ✓
-------------- ---------- --------- ---------- ------- --------
FLEET STATUS: 3/3 aligned
```
If any row shows drift or missing verification:
```sh
# Check which host is out of sync
dot fleet diff
# SSH into the affected host and fix
ssh surface-pro
dot doctor && dot heal
dot update
```
## Step 5: Per-Host Customization
Each host has its own `~/.config/chezmoi/chezmoi.toml` — use it for things that shouldn't propagate:
```toml
# surface-pro's chezmoi.toml
[data]
machine = "surface-pro"
theme = "dome-dark" # per-host default
default_shell = "fish"
terminal_font_size = 11 # smaller for HiDPI
```
```toml
# geekom-a9's chezmoi.toml
[data]
machine = "geekom-a9"
theme = "valley-dark" # different default
default_shell = "zsh"
terminal_font_size = 14 # larger for external 1440p
```
Both hosts render from the same templates, producing correct platform-appropriate output.
## Step 6: Fleet-Wide Updates
When you push a commit to master, each host pulls independently:
```sh
# On each host
dot update
```
`dot update` does:
1. `git pull --rebase --autostash` in `~/.dotfiles`
2. `chezmoi apply` with progress output
3. `dot heal` if apply detected drift
4. Reports changes applied
For simultaneous update across the fleet (requires SSH):
```sh
dot fleet sync
# Connects to each host in parallel
# Runs `dot update` via SSH
# Collects outcomes
```
## Step 7: Secrets Rotation
When a secret is compromised (or as part of routine rotation):
```sh
# 1. Edit the secret on any host
sops dot_config/credentials.sops.yaml
# Change the compromised value, save
# 2. Commit and push
git commit -sS -am "chore(secrets): rotate stripe key"
git push
# 3. All fleet hosts pick up the new value on next update
# On each host:
dot update
```
Since hosts all have the Age private key for themselves, decryption is automatic.
## Step 8: Decommission a Host
When retiring a host:
```sh
# 1. Remove the host's public key from allowed_signers
# Edit dot_ssh/allowed_signers.tmpl (on any host)
# 2. Remove the host's Age public key from .sops.yaml
# 3. Re-encrypt all SOPS files (remove the decommissioned recipient)
find . -name '*.sops.yaml' -exec sops updatekeys {} \;
# 4. Commit
git commit -sS -am "chore(fleet): decommission geekom-a9"
git push
# 5. On the decommissioned host: wipe the dotfiles state
rm -rf ~/.dotfiles ~/.local/bin/dot ~/.config/chezmoi ~/.config/age
```
## Troubleshooting
### "Fleet host unreachable"
`dot fleet` couldn't SSH to a host. Check:
```sh
ssh surface-pro 'dot version'
# If this fails, fix SSH connectivity first
```
### Drift on One Host
Run `dot heal` on that host:
```sh
ssh surface-pro 'dot heal'
```
If drift persists:
```sh
ssh surface-pro 'chezmoi diff | head -50'
# Inspect what's different
```
### Policy Hash Mismatch
One host has an older policy than the others. Usually means that host missed an update:
```sh
ssh surface-pro 'dot update'
```
### Attestation Signature Failed
The host's SSH key isn't in `~/.ssh/allowed_signers` on the verifying host. Fix by redistributing `allowed_signers`.
## Summary
A three-host fleet:
- Installs from one repository
- Has independent signing keys (each host verifies commits from others)
- Can share secrets across specific hosts via SOPS recipients
- Is auditable via `dot fleet attest`
- Updates independently with `dot update` (or in parallel with `dot fleet sync`)
## Next
- [Concept: Fleet Architecture](../01-concepts/04-fleet.md)
- [Reference: Fleet Commands](../03-reference/01-dot-cli.md#fleet)
- [Operations: Fleet Deployment](../../architecture/fleet-deployment.md)