Show HN: Lnk – Git-native dotfiles manager

69 yar-kravtsov 47 5/24/2025, 11:41:19 AM github.com ↗
Move dotfiles to ~/.config/lnk, get symlinks back, use Git normally. Single binary, no config files, no fluff. Built because chezmoi was too complex and plain Git was too manual.

Comments (47)

efitz · 7h ago
I have a feature suggestion:

When you ‘lnk add’ a file, check it for patterns that might indicate it contains a secret, and warn the user if indications of a secret are found. Require —force or something to override.

tough · 6h ago
should be fine on private repos tho
adamors · 3h ago
Most people have public dotfiles actually
tough · 3h ago
Isnt that https://en.wikipedia.org/wiki/Survivorship_bias

we only see those dotfiles that are public so how can we know?

eddd-ddde · 1h ago
In fact, my estimates are that 100% of popular dotfiles repos are public.
wanderingmind · 8h ago
There are so many secrets spread across dot files. Is it possible to encrypt and store them in remote and de-encrypt when it’s pulled to local machines?
shoeffner · 5h ago
I use pass to manage such things separately. This allows to script around hard-coded secrets:

     export MY_TOKEN="$(pass token | head -n 1)"
whilenot-dev · 7h ago
You might be interested in sops: https://github.com/getsops/sops
hk1337 · 6h ago
I avoid putting secrets into dotfiles and try to avoid putting non-secret, personal references in dotfiles. If I really have to, I would start with making the repository private.
OptionX · 8h ago
I don't have many, but the few I do are set in environment vars by a file sourced in my bashrc.

Then you can just not track that file in the tool and figure out a safe way to back it up.

drewcoo · 8h ago
That moves the problem to "how do we securely manage shared keys."

And it adds "how safe are those encrypted secrets [edit: changed from "keys" to more general language] that are committed?" and "what about previous revisions . . . because it's version control?" and "are we sure we're managing offboarding securely?"

There are probably other concerns but those are the ones the immediately shout at me.

hk1337 · 6h ago
Keybase.io was a good option at one point
porridgeraisin · 54m ago
Curious, what happened to keyvase?
peterhadlaw · 8h ago
How is this different from GNU stow?
yar-kravtsov · 8h ago
Great question! GNU Stow and lnk solve similar problems but with different approaches:

GNU Stow: - Package-based structure: Requires organizing dotfiles into "packages" (subdirectories) - Symlink-only: Just handles symlinking, no version control integration - Manual Git: You manage Git separately (git clone → stow package-name) - Perl dependency: Requires Perl to be installed - No safety net: No atomic operations or rollback if something goes wrong

lnk: - File-based workflow: Takes your existing dotfiles as-is, moves them to managed location - Git-integrated: Wraps Git commands (lnk push, lnk pull) for seamless workflow - Atomic operations: If something fails, automatically rolls back changes - Single binary: No dependencies, just download and run - New machine workflow: lnk init -r repo && lnk pull handles clone + restore in one step

Key Difference in Workflow:

Stow approach: # Organize files into packages first mkdir -p ~/.dotfiles/vim/.config/nvim mv ~/.config/nvim/init.lua ~/.dotfiles/vim/.config/nvim/ git add . && git commit # On new machine: git clone repo ~/.dotfiles stow vim # Creates symlinks

lnk approach: # Start with files where they are lnk add ~/.config/nvim/init.lua # Moves and links automatically lnk push "added nvim config" # On new machine: lnk init -r repo && lnk pull # Clone + restore in one command

Bottom line: Stow is a pure symlinking tool that you combine with Git manually. lnk is an opinionated workflow that handles the entire dotfiles lifecycle (move → version → sync → restore).

No comments yet

m000 · 7h ago
My main gripe with dotfile managers (including lnk) is that they assume a uniform environment. I haven't found one that doesn't make this fundamendal assumption.

Some scenarios where dot fils may differ between computers:

- My .gitconfig is different on my work laptop than my desktop.

- I don't have neovim installed on my pi zero running DNS for my home network.

- My zsh functions for making animated gifs won't work if specific tools are not installed.

- An alias to open an image with the default image viewer is different between macos and linux.

- I only have rust toolchain installed on my home desktop, so I shouldn't see it in my PATH on my work laptop.

Is there any solution out there that can handle similar cases? Or are these requirements unique to me? (I don't quite believe they are.)

Galanwe · 7h ago
I believe most dotfile managers offer the possibility to switch config based on hosts (e.g. chez-moi has templates https://www.chezmoi.io/user-guide/manage-machine-to-machine-... ). I guess with lnk being git centric that would be through branches.

In any case you can manage your bashrc/profile and handle conditional logic to install packages there.

ljm · 5h ago
Git supports conditional configuration so you can tell it to load different config files based on file system location or what have you. I use it to swap signing keys and author info depending on whether the repo is personal or for work based.
philjackson · 7h ago
Check out YADM which allows you to link files based on criteria such as hostname.
yonatan8070 · 7h ago
For shell stuff, I use fish, and my fish config has a bunch of if...end blocks around most things, for example:

```if command -q exa alias ls="exa" alias ll="exa -lah" alias tree="exa --tree" end

if command -q eza alias ls="eza" alias ll="eza -lah" alias tree="eza --tree" end```

setopt · 5h ago
This plugin simplifies that workflow:

https://github.com/jabirali/fish-abbrfile

yonatan8070 · 2h ago
That's pretty neat, thanks!
hk1337 · 6h ago
Fish seems to keep it contained to .config/fish which is nice. Most others tend to spread out outside of .config

I’ve started modeling fish with zsh and OMZ customs

justusthane · 7h ago
I handled this by just writing a simple install script that symlinks different files depending the OS (but could use any criteria—hostname, installed packages, etc). Updating the dot files is just a matter of a git pull. If I’ve added a new dotfile, then I run the install script again.
SuperCuber · 6h ago
danlitt · 6h ago
It is significantly more complicated, but what about a symlink manager driven by a programming language, such as guix home or nix's home-manager?
globular-toast · 8h ago
I do the git branch thing with GNU stow and it absolutely sucks, but it's the only thing that sucks. This seems to solve different problems that I don't have but not the one that I do. I've been meaning to try chezmoi specifically because managing machine-specific branches sucks.
amar1729 · 7h ago
I switched over to chezmoi a few years ago - previously had been managing a "main" (macOS) and "Ubuntu" branch for dots. turned out to be a huge pain especially once I ran into work-specific config, or wanted to try other Linux flavors. figuring out the templating and ignore rules to [merge everything](https://github.com/Amar1729/dotfiles/commit/00177e45b09bba80...) took about a weekend's effort but it was so worth it
retrodaredevil · 4h ago
I have a Makefile that runs certain stow commands depending on the hostname of whatever machine I'm on. I keep machine specific files in their own directory, and I also have a shared directory for shared dotfiles. It works well for my needs.
NicolaiS · 5h ago
Another approach that avoids symlinks and avoids a git repo in $HOME (i.e. everything is a subdir of that git repo) is to use the option "git-dir" to clone into a subdir, e.g. `$HOME/.dotfiles/` but checkout into $HOME:

    git clone --bare git@github.com:.../dotfiles.git $HOME/.dotfiles
    git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME checkout
    alias dotfiles='git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
    dotfiles config --local status.showUntrackedFiles no
Now the "dotfiles"-alias can be used like git, "dotfiles add, checkout, log", no symlinks are needed and you avoid "contaminating" subdirs of $HOME (git searches parent dirs for ".git", so a ".git" folder in $HOME kinda sucks)
andy24 · 4h ago
I use this approach as well, works absolutely great for me.
abound · 3h ago
Same, I think I originally got it from the Arch Linux wiki[1], which explains it well.

It in turn references HN, to come full circle.

[1] https://wiki.archlinux.org/title/Dotfiles#Tracking_dotfiles_...

nrvn · 7h ago
I have been using the "bare git at $HOMEDIR"[0] approach for several years.

Benefits:

1. no extra tools.

2. one off task for setting up the git repo and alias `dotfiles='/usr/bin/git --git-dir=${HOME}/.config/dotfiles --work-tree=${HOME}'`

3. all files are where they are, no symlinks, copies, etc.

Caveats:

1. $HOME/.gitignore just ignores everything because it contains a single "*" char[1]. So adding new files must be done with dotfiles add -f ~/.newfile to track.

Refs:

[0] - https://www.atlassian.com/git/tutorials/dotfiles [1] - https://github.com/nrvnrvn/dotfiles/blob/main/.gitignore

akdev1l · 6h ago
I like this a lot. The caveat is not that bad because anyway with stow or other solutions you still need to add things manually.

I believe one could even add stuff like `!.bashrc` to the gitignore so it can keep track of these files automatically. Hence we could have template .gitignore for most common use cases.

hk1337 · 6h ago
I don’t do the * in the gitignore because I use the confit alias and disable showing untracked files. That way I can use the .gitignore in the home as my global gitignore

I love the bare repo method though. I’ve been using it for several years and haven’t had a need for another

yencabulator · 23m ago
You can use ~/.config/git/ignore (core.excludesFile).
hk1337 · 6h ago
I like the method I saw on atlassian and haven’t needed any other option.

Clone to a bare repo and make your home directory the git home. Then I can easily edit the files and push changes.

https://www.atlassian.com/git/tutorials/dotfiles

The whole symlink thing seems like a kludgey mess and pain in the ass to manage

Even GNU Stow seems like a pain

mathstuf · 6h ago
One big benefit of symlinks (really, "not storing it in the deployment") is that my Git repo doesn't have a bunch of hidden files in it because they can appear in the link's path rather than the repo's path. I can also split up files based on "why it exists" rather than "where it lives". For example, I can have the "enable Rust support" group of configurations:

- add Rust things to the list of packages to install - add any Rust-specific configurations for Neovim and `zsh` - bring in any Rust-oriented Neovim plugins

These files all then live next to each other rather than being scatter-shot between Neovim, zsh, and some giant list of packages. Additionally, if I later decide to disable "Rust support" on a machine, the broken symlinks in `$HOME` let me clean up easily without having to actually excise things from the repository.

That said, I have my own system that I built up years ago and it's never been abstracted out for anyone else to use so of course it's going to fit my needs better than anything else.

threecheese · 6h ago
Interesting; these methods seem to follow the same pattern. They both track specific files in a controlled bare git repo, and both transparently link a $HOME file into that repo. Differences are the git method “links” - within the work tree - while ‘lnk’ creates a filesystem link. They both require a special command - a shell alias, vs an installed binary, and both indirectly leverage git subcommands (via the shell alias or the binary).

An advantage of the git method is it doesn’t touch $HOME, and so if the git repo gets wiped out there’s no harm. Am I missing anything?

dmacvicar · 7h ago
I have managed my dotfiles in git for over a decade, and I have never needed anything else than git and GNU stow.

I organize dotfiles in a few modules, and then I stand in the top of the git checkout and do: "stow modulename", and it will symlink everything.

If you don't like separate dotfiles in modules/packages, you can just have one (e.g. "main").

Stow has been around since 1993 or something, so I expect to be around for a while in my distro.

danlitt · 6h ago
The only thing I don't like about stow is that I can't move files around easily. If I decide to move some files around within modules, I have to unstow, then move them, the stow again. If I forget then it leaves dangling symlinks hanging around.
keybored · 7h ago
Same for me.

I don’t remember a problem with dotfiles. I’m sure there were ones that maybe I got used to.

I also use it for ~/bin scripts. There the limitations are more noticable.

Stow plus Git is a setup that already “doesn't suck.” Whatever that means here.

sam_lowry_ · 8h ago
I have chosen the following approach to handle dotfiles:

    1. `git init`
    2.  add * into .gitignore
    3. `git add -f` dotfile
The rest is normal git.

If a dotfile differs by computer, differences live in their own branches and are regulary rebased on the common branch.

No comments yet

dcre · 7h ago
This looks fine to use, but considering you can a achieve the same with a git repo and a 20 line bash script to make the symlinks[0], it seems to me precisely the kind of thing beginners should learn to do themselves rather than trust the magic and assume there’s something complicated going on they couldn’t understand. The fact that all of the commands are the same as the underlying git commands is probably better than the alternative (a bunch of random stuff) but it also makes clear you could do the same with git directly.

[0]: https://crespo.business/posts/version-your-dotfiles/

No comments yet

SuperCuber · 6h ago
Funny that I see this post right after refreshing the documentation on my own project, Dotter[1]

It offers a more involved approach to managing the dotfiles, but (imo) addresses the need to have differences between dotfiles on different machines.

[1]: https://github.com/SuperCuber/dotter

No comments yet

denysvitali · 8h ago
Seems very similar (but less mature) to Chezmoi. How is Chezmoi too complex for your use case? I'm puzzled

No comments yet