A dotbot plugin for decrypting age-encrypted secrets and rendering templates into your dotfiles.
Dotfiles repos often need secrets — SSH configs, API keys, AWS credentials. You want them version-controlled but encrypted. This plugin adds a decrypt directive to dotbot that:
- Decrypts an age-encrypted
KEY=VALUEfile - Renders templates with
{{PLACEHOLDER}}substitution - Outputs to a private directory (gitignored)
- Sets proper permissions (700 dirs, 600 files)
- Supports
--dry-run - Is idempotent (skips unchanged files)
Dotbot symlinks (e.g. ~/.ssh/config) point to files in private/. These files are rendered from templates in encrypted/ by substituting {{PLACEHOLDERS}} with values from the encrypted secrets file.
When you run ./edit-secrets, the script re-encrypts your changes and immediately re-renders the templates into private/. Since the symlinks already point there, your tools pick up the new values right away — no need to re-run ./install.
./install is only needed when you add new symlinks or set up a new machine.
Install age:
# macOS
brew install age
# Linux
apt install age # or see https://github.com/FiloSottile/age#installationgit submodule add https://github.com/fcatuhe/dotbot-age.gitAdd to your .gitignore:
encrypted/age.key
private/
cp dotbot-age/tools/edit-secrets .mkdir -p encrypted
age-keygen -o encrypted/age.keyCreate template files in subdirectories of encrypted/ using {{PLACEHOLDER}} syntax:
encrypted/
├── age.key # gitignored
├── secrets.env.age # committed (encrypted)
├── ssh/
│ └── config # template
└── aws/
└── config # template
Example encrypted/ssh/config:
Host myserver
HostName {{SSH_SERVER_IP}}
User {{SSH_SERVER_USER}}
IdentityFile ~/.ssh/id_ed25519
./edit-secretsThis opens your $EDITOR with a KEY=VALUE file. Add your secrets:
SSH_SERVER_IP=192.168.1.100
SSH_SERVER_USER=admin
AWS_ACCOUNT_ID=123456789
API_KEY=sk-secret-keyOn save, the file is encrypted to encrypted/secrets.env.age and templates are automatically re-rendered into private/.
In your install.conf.yaml:
- plugins:
- dotbot-age
- decrypt: true
- link:
~/.ssh/config: private/ssh/config
~/.aws/config: private/aws/configThe plugin works with zero configuration if you follow these conventions:
| Path | Purpose |
|---|---|
encrypted/age.key |
Age identity (private key, gitignored) |
encrypted/secrets.env.age |
Encrypted KEY=VALUE secrets (committed) |
encrypted/*/ |
Template files with {{PLACEHOLDERS}} |
private/ |
Rendered output (gitignored, created by plugin) |
All conventions can be overridden:
- decrypt:
key: path/to/identity.key
vars: path/to/secrets.env.age
encrypted: path/to/templates/
private: path/to/output/git clone <your-dotfiles-repo> ~/.dotfiles
cd ~/.dotfiles
# Copy age.key from a secure location (password manager, USB key, etc.)
cp /secure/location/age.key encrypted/age.key
./install./edit-secretsThat's it — templates are re-rendered automatically. No need to run ./install.
- Create a file in
encrypted/<category>/<filename>with{{PLACEHOLDERS}} - Add the corresponding link in
install.conf.yaml:~/.config/thing: private/<category>/<filename> - Run
./install
This software is released under the MIT License.