An opinionated symlink manager for dotfiles.
- Simple CLI - Subcommand-based interface (
lnk <command> [flags] <source-dir>) - Recursive file linking - Links individual files throughout directory trees
- Flexible organization - Support for multiple source directories
- Safety first - Dry-run mode and clear status reporting
- Ignore patterns - Optional
.lnkignorefile with gitignore syntax - No dependencies - Single binary, stdlib only (git integration optional)
brew install cpplain/tap/lnk# Create links from current directory
cd ~/git/dotfiles
lnk create . # Link everything from current directory
# Link from a specific subdirectory
lnk create home # Link everything from home/ subdirectory
# Link from an absolute path
lnk create ~/git/dotfiles # Link from specific pathlnk <command> [flags] <source-dir>| Command | Args | Description |
|---|---|---|
create |
<source-dir> |
Create symlinks from source to target |
remove |
<source-dir> |
Remove managed symlinks |
status |
<source-dir> |
Show status of managed symlinks |
prune |
<source-dir> |
Remove broken symlinks |
adopt |
<source-dir> <path...> |
Adopt files into source directory |
orphan |
<source-dir> <path...> |
Remove files from management |
For all commands, source-dir is the first required positional argument (the dotfiles directory). The target directory is always ~.
For adopt/orphan: one or more file or directory paths within ~ are required as additional positional arguments.
| Flag | Description |
|---|---|
--ignore PATTERN |
Additional ignore pattern (repeatable, only affects create) |
-n, --dry-run |
Preview changes without making them |
-v, --verbose |
Enable verbose output |
--no-color |
Disable colored output |
-V, --version |
Show version information |
-h, --help |
Show help message |
# Link from current directory
lnk create .
# Link from a subdirectory
lnk create home
# Link from absolute path
lnk create ~/git/dotfiles
# Dry-run to preview changes
lnk create -n .
# Add ignore pattern
lnk create --ignore '*.swp' .# Remove links from source directory
lnk remove .
# Remove links from subdirectory
lnk remove home
# Dry-run to preview removal
lnk remove -n .# Show status of links from current directory
lnk status .
# Show status from subdirectory
lnk status home
# Show status with verbose output
lnk status -v .# Remove broken symlinks from current directory
lnk prune .
# Remove broken symlinks from specific source
lnk prune home
# Dry-run to preview pruning
lnk prune -n .# Adopt files into current directory
lnk adopt . ~/.bashrc ~/.vimrc
# Adopt into specific source directory
lnk adopt ~/git/dotfiles ~/.bashrc
# Adopt with dry-run
lnk adopt -n . ~/.gitconfig# Remove file from management (current directory as source)
lnk orphan . ~/.bashrc
# Orphan with specific source
lnk orphan ~/git/dotfiles ~/.bashrc
# Dry-run to preview orphaning
lnk orphan -n . ~/.config/oldapplnk supports an optional ignore file in your source directory.
Place in source directory. Gitignore syntax for files to exclude from linking.
.git
*.swp
*~
README.md
scripts/
.DS_Store
lnk automatically ignores these patterns:
.git.gitignore.DS_Store*.swp*.tmpREADME*LICENSE*CHANGELOG*.lnkignore
lnk recursively traverses your source directory and creates individual symlinks for each file (not directories). This approach:
- Allows you to mix managed and unmanaged files in the same target directory
- Preserves your ability to have local-only files alongside managed configs
- Creates parent directories as needed (never as symlinks)
Example: Linking from ~/dotfiles to ~:
Source: Target:
~/dotfiles/
.bashrc → ~/.bashrc (symlink)
.config/
git/
config → ~/.config/git/config (symlink)
nvim/
init.vim → ~/.config/nvim/init.vim (symlink)
The directories .config, .config/git, and .config/nvim are created as regular directories, not symlinks. This allows you to have local configs in ~/.config/localapp/ that aren't managed by lnk.
You can organize your dotfiles in different ways:
Flat Repository:
~/dotfiles/
.bashrc
.vimrc
.gitconfig
Use: lnk create . from within the directory
Nested Repository:
~/dotfiles/
home/ # Public configs
.bashrc
.vimrc
private/ # Private configs (e.g., git submodule)
.ssh/
config
Use: lnk create home to link public configs, or lnk create ~/dotfiles/private for private configs
The target directory is always ~ and is not configurable.
For ignore patterns: all sources are combined — built-in defaults, .lnkignore,
and --ignore flags are all merged into a single pattern list.
lnk supports gitignore-style patterns for excluding files from linking:
*.swp- all swap fileslocal/- local directory!important.swp- negation (include this specific file)**/*.log- all .log files recursively
Patterns can be specified via:
.lnkignorefile (one pattern per line)- CLI flags (
--ignore pattern)
# 1. Clone your dotfiles
git clone https://github.com/you/dotfiles.git ~/dotfiles
cd ~/dotfiles
# 2. If using private submodule
git submodule update --init
# 3. Create links (dry-run first to preview)
lnk create -n .
# 4. Create links for real
lnk create .# Adopt a new app config into your repository (current directory as source)
lnk adopt . ~/.config/newapp
# This will:
# 1. Move ~/.config/newapp to ./.config/newapp
# 2. Create symlinks for each file in the directory tree
# 3. Preserve the directory structure
# Or specify source directory explicitly
lnk adopt ~/dotfiles ~/.config/newapp# Keep work/private configs separate using git submodule
cd ~/dotfiles
git submodule add [email protected]:you/dotfiles-private.git private
# Structure:
# ~/dotfiles/public/ (public configs)
# ~/dotfiles/private/ (private configs via submodule)
# Link public configs
cd ~/dotfiles
lnk create public
# Link private configs
lnk create private
# Or adopt to appropriate location
lnk adopt ~/dotfiles/public ~/.bashrc # Public config
lnk adopt ~/dotfiles/private ~/.ssh/config # Private config# 1. Remove existing links from old manager
stow -D home # Example: GNU Stow
# 2. Create links with lnk
cd ~/dotfiles
lnk create .
# lnk creates individual file symlinks instead of directory symlinks,
# so you can gradually migrate and test- Always dry-run first - Use
--dry-runor-nto preview changes before making them - Check status regularly - Use
lnk status .to check for broken links - Organize your dotfiles - Separate public and private configs into subdirectories
- Leverage .lnkignore - Exclude build artifacts, local configs, and README files
- Test on VM first - When setting up a new machine, test in a VM before production
- Version your configs - Keep
.lnkignorein git for reproducibility - Use verbose mode for debugging - Add
-vto see what lnk is doing
- lnk: Creates individual file symlinks, allows mixing configs from multiple sources
- stow: Creates directory symlinks, simpler but less flexible
- lnk: Simple symlinks, no templates, what you see is what you get
- chezmoi: Templates, encryption, complex state management
- lnk: Subcommand-based CLI, built-in adopt/orphan operations
- dotbot: YAML-based config, more explicit control
lnk is designed for users who:
- Want a simple, subcommand-based CLI
- Prefer symlinks over copying
- Need to mix public and private configs
- Want built-in adopt/orphan workflows
- Value clarity over configurability
# Remove old links (from original location)
cd /old/path/to/dotfiles
lnk remove .
# Recreate from new location
cd /new/path/to/dotfiles
lnk create .# Check if they're ignored
lnk create -v . # Verbose mode shows ignored files
# Check .lnkignore
cat .lnkignore# Check file permissions in source
ls -la ~/dotfiles/.ssh
# Files should be readable
chmod 600 ~/dotfiles/.ssh/configMIT License - see LICENSE file for details.
See CONTRIBUTING.md for development setup and guidelines.