Persisting Zsh history across container rebuilds
Every DevContainer rebuild discards the container’s writable layer, and with it ~/.zsh_history — so your hard-won command history resets to empty each time you recreate the environment. This page moves HISTFILE onto a named volume so history survives rebuilds without polluting the image or the workspace. It extends the Shell Environment Customization (Zsh, Fish, Bash) cluster.
Prerequisites
- Zsh as the default shell in the container
- A non-root
remoteUser(e.g.vscode) - Docker named-volume support
How-To Steps
-
Create a dedicated volume for shell state and point
HISTFILEat it:{ "image": "mcr.microsoft.com/devcontainers/base:bookworm@sha256:2c5a...", "mounts": [ "source=zsh-history,target=/commandhistory,type=volume" ], "containerEnv": { "HISTFILE": "/commandhistory/.zsh_history" }, "remoteUser": "vscode" } -
Fix ownership and configure history options on create, since the fresh volume is root-owned:
{ "postCreateCommand": "sudo mkdir -p /commandhistory && sudo chown -R vscode:vscode /commandhistory && echo 'setopt INC_APPEND_HISTORY SHARE_HISTORY' >> ~/.zshrc" }INC_APPEND_HISTORYwrites each command immediately, so history survives even an ungraceful container stop. -
Set generous history limits so the persisted file actually retains depth. Append to
~/.zshrc:export HISTSIZE=100000 export SAVEHIST=100000 -
Verify persistence by rebuilding and checking the count:
wc -l /commandhistory/.zsh_history devcontainer up --workspace-folder . --remove-existing-container devcontainer exec --workspace-folder . wc -l /commandhistory/.zsh_history # unchanged
Common Pitfalls
| Symptom | Root Cause | Remediation |
|---|---|---|
| History empty after rebuild | HISTFILE in the container layer | Point HISTFILE at a named-volume path |
permission denied writing history | Fresh volume owned by root | chown the volume in postCreateCommand |
| Only last session’s commands kept | INC_APPEND_HISTORY not set | Add setopt INC_APPEND_HISTORY SHARE_HISTORY |
| History truncated | Low HISTSIZE/SAVEHIST | Raise both to a high value |
Conclusion
The invariant: HISTFILE must resolve to a path on a persistent volume the remoteUser owns, with incremental append enabled. Do that and command history outlives every rebuild without ever being committed to the image or the repo.
FAQ
Why not store history in the workspace?
The workspace is your repo — history doesn’t belong in version control and would create noise in git status. A dedicated volume keeps it personal and out of the tree.
Does this work for Bash or Fish too?
Yes. Point the shell’s history variable (HISTFILE for Bash, fish_history location for Fish) at the same volume and apply the equivalent append option.
Related
- Shell Environment Customization (Zsh, Fish, Bash) — the parent cluster.
- Setting up custom shell aliases in devcontainer.json — defining aliases alongside history.
- Bootstrapping dotfiles with chezmoi in a DevContainer — managing the rest of your shell config.