Skip to content

Files and directories ​

Files ​

Use dots.file to manage a file from a source in the dots project:

lua
dots.file("~/.ssh/config", {
  source = "ssh/config",
  mode = "0600",
})

Relative source paths are resolved from the directory containing dots.lua.

Dots creates the target when it is missing. If the target already has the same content and mode, dots check shows it for tracking. Tracking records the file in state without rewriting it. A different unmanaged file is a conflict.

The source and any existing target must be regular files. Dots will not replace a directory, symlink, or special file.

Once the file is tracked, changes to the source or mode are applied to the target. Without mode, dots preserves existing permissions; new files use the platform default.

Directories ​

Use dots.directory when a program needs a directory with specific permissions:

lua
local acme = dots.directory("/opt/traefik/acme", {
  mode = "0755",
})

dots.file("/opt/traefik/acme/config.json", {
  source = "traefik/config.json",
  needs = { acme },
})

Both dots.file and dots.directory return references for needs. Here, the directory is created before the file is written.

Dots creates missing directories, including their parents. An existing directory is shown for tracking when its mode matches. A different mode is a conflict until the directory is tracked; after that, dots corrects mode changes. Files, symlinks, and other non-directory targets are conflicts.

Without mode, dots manages only whether the directory exists. The mode applies to the declared directory, not to parents created along the way.

Removing declarations ​

Removing a file declaration schedules the target for deletion. Dots deletes it only when its content and managed mode still match what was applied. If the file was edited outside dots, removal is a conflict.

Removing a directory declaration deletes it when empty. Managed files and directories inside it can be removed in the same apply, but anything else makes removal a conflict. Nested directories count as managed only when they have their own dots.directory(...) declaration. Dots never deletes a directory recursively.

Keep the target ​

To keep /srv/myapp/uploads but stop managing it, first remove its dots.directory(...) declaration, then run:

sh
dots state forget directory:/srv/myapp/uploads

For a file:

sh
dots state forget file:/etc/myapp/config.toml

forget removes only the state entry. The file or directory stays in place. Use dots state list to find the exact entry name. If the declaration remains, the next dots apply offers to track the target again.