Skip to main content
Use .chezmoiexternal.toml to include files from external sources. chezmoi downloads and manages them as part of your source state.

Include a subdirectory from a URL

Import entire repositories like Oh My Zsh without using git submodules:
~/.local/share/chezmoi/.chezmoiexternal.toml
[".oh-my-zsh"]
    type = "archive"
    url = "https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz"
    exact = true
    stripComponents = 1
    refreshPeriod = "168h"
[".oh-my-zsh/custom/plugins/zsh-syntax-highlighting"]
    type = "archive"
    url = "https://github.com/zsh-users/zsh-syntax-highlighting/archive/master.tar.gz"
    exact = true
    stripComponents = 1
    refreshPeriod = "168h"
[".oh-my-zsh/custom/themes/powerlevel10k"]
    type = "archive"
    url = "https://github.com/romkatv/powerlevel10k/archive/v1.15.0.tar.gz"
    exact = true
    stripComponents = 1
1

Apply changes

chezmoi apply
chezmoi downloads and unpacks archives automatically.
2

Force refresh

chezmoi --refresh-externals apply
Forces re-download even within refreshPeriod.

Understanding refresh periods

With refreshPeriod

refreshPeriod = "168h"  # 1 week
For URLs pointing to moving targets (e.g., master branch)

Without refreshPeriod

# No refreshPeriod set
For URLs pointing to tagged versions (e.g., v1.15.0)
When using Oh My Zsh, disable auto-updates by setting DISABLE_AUTO_UPDATE="true" in ~/.zshrc. Auto-updates will cause the directory to drift from chezmoi’s source state.
If externals have cache files that change during use, add the cache directory to .chezmoiignore. For example, Oh My Zsh caches completions in .oh-my-zsh/cache/completions/.
Don’t use externals for large files or archives. chezmoi validates exact contents every time you run diff, apply, or verify. For large externals, use a run_onchange_ script instead.

Include a subdirectory with selected files from a URL

Use include filters to import only specific files:
~/.local/share/chezmoi/.chezmoiexternal.toml
[".oh-my-zsh/custom/plugins/zsh-syntax-highlighting"]
    type = "archive"
    url = "https://github.com/zsh-users/zsh-syntax-highlighting/archive/master.tar.gz"
    exact = true
    stripComponents = 1
    refreshPeriod = "168h"
    include = ["*/*.zsh", "*/.version", "*/.revision-hash", "*/highlighters/**"]
This imports only the required source files, reducing the size of your dotfiles repo.

Include a single file from a URL

Use type = "file" for single files:
~/.local/share/chezmoi/.chezmoiexternal.toml
[".vim/autoload/plug.vim"]
    type = "file"
    url = "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim"
    refreshPeriod = "168h"
[".vim/autoload/plug.vim"]
    type = "file"
    url = "https://raw.githubusercontent.com/junegunn/vim-plug/0.12.0/plug.vim"
No refreshPeriod needed for tagged versions.

Extract a single file from an archive

Extract specific files from archives:
~/.local/share/chezmoi/.chezmoiexternal.toml
{{ $ageVersion := "1.1.1" -}}
[".local/bin/age"]
    type = "archive-file"
    url = "https://github.com/FiloSottile/age/releases/download/v{{ $ageVersion }}/age-v{{ $ageVersion }}-{{ .chezmoi.os }}-{{ .chezmoi.arch }}.tar.gz"
    path = "age/age"
This:
  1. Downloads the archive for your OS and architecture
  2. Extracts only the age/age member
  3. Places it at ~/.local/bin/age
Templates in .chezmoiexternal.toml allow platform-specific URLs.

Import archives

For one-time imports, use the import command:
1

Download archive

curl -s -L -o ${TMPDIR}/oh-my-zsh-master.tar.gz https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz
2

Create destination directory

mkdir -p $(chezmoi source-path)/dot_oh-my-zsh
3

Import archive

chezmoi import --strip-components 1 --destination ~/.oh-my-zsh ${TMPDIR}/oh-my-zsh-master.tar.gz
4

Apply changes

chezmoi apply
import updates the source state but doesn’t apply changes. Run chezmoi apply to update your home directory.

Handle tar archives in unsupported compression formats

chezmoi natively supports: bzip2, gzip, xz, and zstd. For other formats, use filters:
~/.local/share/chezmoi/.chezmoiexternal.toml
[".Software/anki/2.1.54-qt6"]
    type = "archive"
    url = "https://github.com/ankitects/anki/releases/download/2.1.54/anki-2.1.54-linux-qt6.tar.zst"
    filter.command = "zstd"
    filter.args = ["-d"]
    format = "tar"
How it works:
  1. filter.command and filter.args pipe downloaded data through zstd -d
  2. format = "tar" tells chezmoi the filter outputs uncompressed tar

Include a subdirectory from a git repository

Manage git repositories directly:
~/.local/share/chezmoi/.chezmoiexternal.toml
[".vim/pack/alker0/chezmoi.vim"]
    type = "git-repo"
    url = "https://github.com/alker0/chezmoi.vim.git"
    refreshPeriod = "168h"
Behavior:
  • First run: git clone
  • Subsequent runs: git pull (respects refreshPeriod)
  • Force refresh: chezmoi -R apply
chezmoi only runs git clone/git pull. You must have git in your $PATH. chezmoi cannot manage other files in the directory, and contents won’t appear in chezmoi diff or chezmoi dump.

Customize git commands

~/.local/share/chezmoi/.chezmoiexternal.toml
[".vim/pack/alker0/chezmoi.vim"]
    type = "git-repo"
    url = "https://github.com/alker0/chezmoi.vim.git"
    refreshPeriod = "168h"
    [".vim/pack/alker0/chezmoi.vim".pull]
        args = ["--ff-only"]
If you need to manage extra files in a git repository, use type = "archive" with a URL pointing to the master/main branch archive instead.

Use git submodules in your source directory

If using git submodules, set the external_ attribute on directories containing submodules.
chezmoi supports git submodules with --recurse-submodules by default:
1

Add submodule

chezmoi cd
git submodule add https://github.com/user/repo.git external_repo
2

Set external_ attribute

# Name the directory with external_ prefix
mv repo external_repo
This prevents chezmoi from interpreting filenames like run_test.sh as scripts.
3

Initialize and update

chezmoi init --recurse-submodules https://github.com/user/dotfiles.git
Disable submodule handling with --recurse-submodules=false or set update.recurseSubmodules = false in your config.

Comparison: External types

TypeUse caseRefreshManaged by chezmoi
archiveFull repository/directoryYesYes
fileSingle fileYesYes
archive-fileSingle file from archiveYesYes
git-repoGit repositoryYesNo (git manages)
git submoduleGit repositoryYesNo (git manages)

Build docs developers (and LLMs) love