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
Apply changes
chezmoi downloads and unpacks archives automatically.
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 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"
Static version
Latest version
[ " . 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. [ " . vim/autoload/plug . vim" ]
type = "file"
url = "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim"
refreshPeriod = "168h"
Use refreshPeriod to check for updates.
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:
Downloads the archive for your OS and architecture
Extracts only the age/age member
Places it at ~/.local/bin/age
Templates in .chezmoiexternal.toml allow platform-specific URLs.
Import archives
For one-time imports, use the import command:
Download archive
curl -s -L -o ${ TMPDIR } /oh-my-zsh-master.tar.gz https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz
Create destination directory
mkdir -p $( chezmoi source-path ) /dot_oh-my-zsh
Import archive
chezmoi import --strip-components 1 --destination ~/.oh-my-zsh ${ TMPDIR } /oh-my-zsh-master.tar.gz
import updates the source state but doesn’t apply changes. Run chezmoi apply to update your home directory.
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:
filter.command and filter.args pipe downloaded data through zstd -d
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:
Add submodule
chezmoi cd
git submodule add https://github.com/user/repo.git external_repo
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.
Initialize and update
chezmoi init
chezmoi 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
Type Use case Refresh Managed by chezmoi archiveFull repository/directory Yes Yes fileSingle file Yes Yes archive-fileSingle file from archive Yes Yes git-repoGit repository Yes No (git manages) git submodule Git repository Yes No (git manages)