Skip to content

Deploy Dotfiles

A garden can hold your dotfile sources and put them where they belong on the machine, the job GNU Stow or bombadil link does. You declare a source-to-target table in config/deploy.toml; softfig deploy materializes it, conflict-safe and dry-runnable. Deploy moves files as-is: there is no templating yet. See the limits before you plan around it.

Deploy sources live under config/source/ in the garden; the table maps each one to a target. The schema is deliberately small — a [dots] table of named entries:

[dots]
bashrc = { source = "bashrc", target = ".bashrc" }
wayfire = { source = "wayfire.ini", target = ".config/wayfire.ini" }
weather = { source = "weather.conf", target = ".config/weather/weather.conf", method = "copy" }
  • source is relative to config/source/.
  • target is relative to $HOME (or an absolute path under $HOME).
  • method is symlink (the default) or copy.

A target that resolves inside the garden itself is refused.

Entry names may use letters, digits, ., _ and -. The plan is reported in name order. Unknown keys in the file are rejected, so a typo fails loudly.

symlink (default). The source is materialized into a stable deploy-cache and the target becomes a symlink to it. Why the cache? The garden’s plaintext is a FUSE mount that vanishes the instant the garden locks — a link straight into it would dangle. The cache (0700/0600, under your data dir) is the durable thing the link points at, so your dotfiles survive a locked garden.

copy. The bytes are written straight to the target with a managed by softfig stamp in a leading comment, e.g.:

# managed by softfig — edits will be overwritten (source: config/source/weather.conf)

Use copy for the handful of apps that refuse to follow a symlinked config. The stamp is a warning to future-you: edits to the target get overwritten on the next deploy, because the source is the truth.

Always dry-run first. It prints the plan and touches nothing:

Terminal window
softfig deploy --dry-run
Plan (3 dot(s)):
symlink bashrc → /home/you/.bashrc
symlink wayfire → /home/you/.config/wayfire.ini
copy weather → /home/you/.config/weather/weather.conf
(dry run — nothing changed)

Each row’s verb is the action apply would take: symlink, copy, replace (refresh a managed target), skip (already up to date), or CONFLICT. When the plan looks right, drop the flag:

Terminal window
softfig deploy

It reports what it did (created, replaced, copied, skipped, forced) and any conflicts. Re-running is safe: up-to-date entries are skipped.

deploy will not silently clobber a file it doesn’t manage. If a target already exists as an unmanaged file, a foreign symlink, or a directory, that row is marked CONFLICT and skipped, and the command exits non-zero. To take over such targets, back them up and overwrite in one step:

Terminal window
softfig deploy --force

--force moves each conflicting target to <target>.softfig-bak before writing the new one, so nothing is lost — you can inspect or restore the .softfig-bak copy afterward.

softfig deploy [--dry-run] [--force] [--garden-root PATH] [--cache-root PATH]
  • --garden-root defaults to ~/soft-fig_garden (the FUSE mount to read sources and the table from).
  • --cache-root defaults to $XDG_DATA_HOME/softfig/deployed (i.e. ~/.local/share/softfig/deployed), where symlink targets point. It must be outside the garden.
  • $HOME targets only. Absolute targets outside $HOME (the /etc case) are rejected.
  • Files, not directories. A source must be a single file.
  • No templating, secrets or post-deploy hooks. Sources deploy verbatim. Template rendering with variables, and render-time vault secrets, are planned but not built. Don’t write {{ }} variables in a source expecting them to render.
  • Sealed sources deploy as plaintext. If a source is sealed, deploy decrypts it: the real contents land in the deploy cache and the target, as owner-only (0600) files outside the vault. Treat that as a deliberate declassification, and point such a dot at a target you’re comfortable with.
  • You create the table. softfig onboard scaffolds a config/ directory (daemon and growlight settings), but no deploy.toml or config/source/. Add both when you’re ready to manage a dotfile from the garden.

Related: Seal & Reveal and the CLI reference for the full command surface.