zhi

Export and Templates

zhi exports configuration to standard file formats using Go templates. This is zhi’s provisioning mechanism: rather than embedding specific tool SDKs, zhi renders configuration to files that external tools consume.

Built-in Formats

Export the full tree (filtered to enabled components) in a standard format without writing a template:

zhi export --format json
zhi export --format yaml
zhi export --format toml
zhi export --format dotenv

Write to a file instead of stdout:

zhi export --format json --output config.json

Custom Templates

Templates use Go’s text/template syntax with Sprig functions available.

Example: templates/docker-compose.yml.tmpl

services:
  db:
    environment:
      POSTGRES_HOST: 
      POSTGRES_PORT: 

  redis:
    image: redis:7
    environment:
      REDIS_URL: 

Export with a template:

zhi export --template ./templates/docker-compose.yml.tmpl
zhi export --template ./templates/my.tmpl --output ./output.yml

Workspace Export Configuration

Define export templates in zhi.yaml to run them all at once with zhi export:

export:
  templates:
    - name: docker-compose
      template: ./templates/docker-compose.yml.tmpl
      output: ./docker-compose.override.yml
    - name: env-file
      format: dotenv
      output: ./.env
      prefix: app/env

Running zhi export without flags exports all configured templates. Each entry can use either a custom template file or a built-in format.

Template Functions

Tree Access

Function Description
.Get "path" Get value as string (empty string if missing or disabled component)
.GetOr "path" "default" Get value with fallback
.Has "path" Returns true if path exists and belongs to an enabled component
.All All key-value pairs as map[string]any (filtered to enabled components)
.Prefix "prefix" Key-value pairs under a path prefix as a flat map
.Nested "prefix" Key-value pairs under a prefix as a nested map (split on /)
.Meta "path" "key" Get a metadata value for a path

Component Access

Function Description
.ComponentEnabled "name" true if the named component is enabled
.EnabledComponents []string of enabled component names
.DisabledComponents []string of disabled component names
.ComponentPaths "name" []string of path prefixes for the named component

Format Helpers

Function Description
toJSON Marshal value to indented JSON
toJSONCompact Marshal to compact JSON
toYAML Marshal to YAML
toTOML Marshal to TOML
toDotenv Render flat map as KEY=value lines (uppercased, / replaced with _)
quote Shell-safe quoting
indent Indent a multi-line string by N spaces
upper, lower, replace String manipulation

All Sprig template functions are also available.

File Output Control

These functions control properties of the exported file itself. They render as empty strings in the template output and only take effect when writing to a file (not with --dry-run or stdout).

Function Description
fileACL <entry> Add a POSIX ACL entry via setfacl -m. Can be called multiple times. Requires setfacl on the system.
fileMode <mode> Override file permissions (default 0644). Pass an octal integer, e.g. 0600

Example: restrict a secrets file and grant access to a container group:

DATABASE_PASSWORD=

This is useful for Docker volume host-path mounts where a container-only GID needs access to the file. POSIX ACLs work with numeric GIDs that don’t need to exist on the host. The ACL is also applied to the parent directory (with execute permission added) so that the subject can traverse the directory and delete the file.

Component Awareness

By default, exports only include paths belonging to enabled components and unmanaged paths:

Use --all-components to bypass this filtering:

zhi export --format json --all-components

Examples

Export everything as JSON:


Export as dotenv:

# Generated by zhi

Conditional sections based on components:


monitoring:
  scrape_interval: 
  targets:
    - 

Nested map for a prefix:


Dry Run

Preview exported output without writing files:

zhi export --format json --dry-run
zhi export --template ./templates/my.tmpl --dry-run

Diff and Drift Detection

Compare what zhi would export against the files currently on disk:

zhi export --diff   # Show unified diff for all workspace exports

For continuous monitoring, use zhi drift to detect when exported files have been modified outside of zhi (e.g. manual edits, another process overwriting a config file):

zhi drift                                          # One-shot check
zhi drift --json                                   # Machine-readable output
zhi drift --watch --interval 5m                    # Continuous monitoring
zhi drift --watch --on-drift "notify-send 'drift'" # With notification hook

The drift command re-renders all workspace export templates and compares the result against files on disk. It returns exit code 0 when everything is in sync, 1 when drift is detected, and 2 on errors — making it suitable for CI/CD pipelines.

See CLI Reference for the full flag reference.