Declarative files
The files option allows you to declaratively create configuration files and scripts in your development environment. Files are automatically generated from structured data and symlinked into your project when entering the shell.
This is particularly useful for:
- Generating configuration files from Nix data structures
- Creating executable scripts
- Setting up project-specific configurations
- Ensuring consistent file contents across the team
Supported Formats
Section titled “Supported Formats”devenv supports multiple file formats out of the box:
- json - JSON format
- ini - INI format
- yaml - YAML format
- toml - TOML format
- text - Plain text
Basic Examples
Section titled “Basic Examples”JSON Files
Section titled “JSON Files”{ files."config.json".json = { database = { host = "localhost"; port = 5432; }; features = [ "auth" "api" "ui" ]; };}This creates a config.json file:
{ "database": { "host": "localhost", "port": 5432 }, "features": ["auth", "api", "ui"]}YAML Files
Section titled “YAML Files”{ files."docker-compose.yml".yaml = { version = "3.8"; services = { web = { image = "nginx:latest"; ports = [ "8080:80" ]; }; }; };}TOML Files
Section titled “TOML Files”{ files."config.toml".toml = { title = "My App Config";
server = { host = "0.0.0.0"; port = 8000; }; };}INI Files
Section titled “INI Files”{ files."settings.ini".ini = { general = { debug = "true"; log_level = "info"; }; database = { connection_string = "postgres://localhost/mydb"; }; };}Text Files
Section titled “Text Files”For plain text files, simply provide a string:
{ files."README.txt".text = '' This is a development environment. Run `devenv shell` to get started. '';}Executable Files
Section titled “Executable Files”You can make any file executable by setting the executable attribute:
{ files."setup.sh" = { text = '' #!/bin/bash echo "Running setup..." npm install ''; executable = true; };}This is particularly useful for:
- Shell scripts that need to be executed
- Custom tooling and utilities
- Git hooks
Copying Files Instead of Symlinking
Section titled “Copying Files Instead of Symlinking”By default, files are symlinked to a read-only path in the Nix store, so they cannot be edited.
Set the copyMode attribute to materialize an editable file instead:
{ # Seed an editable template once; never overwrites the user's edits. files.".env.local".copyMode = "seed"; files.".env.local".text = '' API_URL=http://localhost:8080 '';
# Always overwrite with a fresh writable copy on every shell entry. files."config/generated.toml".copyMode = "copy"; files."config/generated.toml".toml = { server.port = 8000; };}The copyMode attribute accepts:
symlink(default): symlink to the read-only file in the Nix store. Edits are not possible; devenv keeps the link pointed at the current contents.seed: copy the file into place once, only if it does not already exist, and make it writable. Existing files are left untouched, so your edits are preserved. This is useful for seeding configuration files from templates that the user can then edit to fit their project.copy: copy the file into place as a writable file, overwriting it with fresh contents on every shell entry. This is useful when a tool must write to the file in place but devenv should remain the source of truth.
Declarative files in Subdirectories
Section titled “Declarative files in Subdirectories”Files can be created in nested directories by specifying the path:
{ files = { ".config/app/settings.json".json = { theme = "dark"; };
"scripts/build.sh" = { text = "#!/bin/bash\nnpm run build"; executable = true; }; };}The parent directories will be created automatically if they don’t exist.

