Skip to content

Instantly share code, notes, and snippets.

@Keithsel
Last active February 13, 2026 15:27
Show Gist options
  • Select an option

  • Save Keithsel/9974f329267d16c78a6f7921eb24e740 to your computer and use it in GitHub Desktop.

Select an option

Save Keithsel/9974f329267d16c78a6f7921eb24e740 to your computer and use it in GitHub Desktop.
Declarative Arch Manifesto

Declarative Arch Manifesto

Declare intent, converge state, move on.

The problem

Arch is imperative. You run commands, packages accumulate, configs scatter. Six months later you can't explain half your system. Reinstalling means archaeology. Syncing machines means drift.

We want repeatability without NixOS complexity. Not hermetic builds, not byte-identical closures. Just a declaration that describes the system, and a tool that makes it so.

We believe

Your system should match what you declare, nothing more. If it's declared, it exists. If it's not declared, it gets removed. No exceptions, no "I'll clean that up later."

If you can't explain why a package exists, it shouldn't. Every package belongs to a module. Every module has a purpose. Loose packages are how systems rot.

Simplicity beats features. We don't handle every edge case. We don't recover gracefully from partial failures. Something broke? Fix your declaration, run sync again. The tool is simple so you can understand it completely.

We practice

  1. Declare everything you want installed. Packages, dotfiles, services, hooks - if it's part of your system, it's in a module.

  2. Let the package manager handle dependencies. You declare what you want, pacman resolves the rest. Optional dependencies you actually use must be declared explicitly.

  3. Remove what you stop declaring. Undeclared packages and orphaned dependencies are removed every sync. Nothing lingers.

  4. One owner per dotfile. Two modules targeting the same path is an error. The sync fails until you fix the conflict.

  5. Declare services alongside packages. If a module needs a service running, say so. Services enable when the module is enabled, disable when it's not.

  6. Write hooks that can run twice. Hooks are tracked after success, but you might reset them. They must tolerate re-runs.

  7. List modules in the order they apply. First listed, first applied. Order matters when it matters.

  8. Trust the declaration over stored state. State exists to compute diffs. When state and declaration disagree, declaration wins.

  9. Expect sync to be repeatable. Run it once or ten times, same result. Clone your config to a new machine, run sync, walk away.

We don't

Pin versions. Arch is rolling. If you want reproducible builds, use a different distro.

Manage /etc declaratively. We symlink dotfiles in your home. System files are out of scope. Hooks exist for when you need to touch /etc - use them, document them.

Replace pacman. We're a layer above. Pacman does the work; we decide what work to do.

Merge file contents. We symlink whole files. If two modules need lines in the same file, refactor into one module or use a hook.

Handle partial failure gracefully. If a hook fails, the sync fails. If a package won't install, the sync fails. Fix the problem, run again. We don't paper over errors.

Not NixOS. Not Ansible. Just enough to sleep well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment