Skip to content

image(debian): move the shell history to a mountable directory - #466

Merged
nozaq merged 1 commit into
mainfrom
claude/bash-history-persistence-w8pkhc
Sep 12, 2026
Merged

nozaq merged 1 commit into
mainfrom
claude/bash-history-persistence-w8pkhc

Conversation

@nozaq

@nozaq nozaq commented Sep 12, 2026

Copy link
Copy Markdown
Member

Lets a dev container keep bash history across rebuilds by mounting a named volume, as in the VS Code persist-bash-history recipe. The templates side is bare-devcontainer/templates#202 and depends on this landing first.

Why the image owns this

A named volume mounts as a directory, so bash's default ~/.bash_history cannot be persisted where it is. Mounting a volume over the home directory instead would shadow everything the image puts there and freeze it at first-start contents, which is the opposite of what these images are for.

So the history file needs a directory of its own, and HISTFILE has to point into it. Both belong to the image, the same way terraform and opentofu already do it for the plugin cache their templates mount:

ENV TF_PLUGIN_CACHE_DIR="/home/dev/.terraform.d/plugin-cache"
RUN mkdir -p "${TF_PLUGIN_CACHE_DIR}"

Creating the directory in the image is also what makes the mount usable: Docker creates a mount point missing from the image as root:root, and the containers run as dev with no sudo and no-new-privileges, so nothing inside could fix it afterwards. The VS Code recipe does the same with mkdir + chown for non-root users.

/home/dev/.local/state/bash follows the XDG Base Directory Specification, which names history as state data under $XDG_STATE_HOME (default $HOME/.local/state). The mise image already uses /home/dev/.local/state/mise.

Changes

  • debian/Dockerfile: set HISTFILE and create its directory, after the USER switch so it belongs to dev.
  • debian/assets/.bashrc: append each command to $HISTFILE as it is entered, composing with any PROMPT_COMMAND a user sets later. histappend alone defers the write to shell exit, which a container stop does not always allow.
  • debian/smoke-test.sh: assert the directory is writable by dev.
  • debian/README.md: document the variable, the directory, and what a dev container does with them.

Everything here is inherited by every image built on this base.

Note for review

HISTFILE now applies to every container from these images, including plain docker run and CI, not only dev containers that mount the volume. Without a mount the history lands in the container layer, exactly as ~/.bash_history would, so nothing breaks; the path is simply no longer bash's default. This is the same trade the TF_PLUGIN_CACHE_DIR images already make.

Verification

Docker is not available in this environment, so the image was not built here. The .bashrc change was verified against bash 5.2 by running an interactive shell with the new rcfile, issuing a command, and killing the shell with SIGKILL: the command is in $HISTFILE afterwards, and with the current .bashrc it is not. build-checks.yml builds the image and runs the new smoke-test assertion.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L6orjBAZq9L5t7rV5RDmXd

@nozaq
nozaq force-pushed the claude/bash-history-persistence-w8pkhc branch from 9b5e719 to e21e48b Compare September 12, 2026 05:27
@nozaq
nozaq force-pushed the claude/bash-history-persistence-w8pkhc branch from e21e48b to 377df5e Compare September 12, 2026 05:29
@nozaq
nozaq marked this pull request as ready for review September 12, 2026 05:31
Dev container templates can keep bash history across rebuilds by mounting a
named volume, but the history file has to sit in a directory of its own for
that: a named volume cannot be mounted over ~/.bash_history, and mounting one
over the home directory would shadow everything the image puts there.

Set HISTFILE to /home/dev/.local/state/bash/history and create that directory,
the way the terraform and opentofu images set TF_PLUGIN_CACHE_DIR and create
the directory their templates mount. The path follows the XDG Base Directory
specification, which names history as state data under $XDG_STATE_HOME.
Creating the directory in the image also makes the mount belong to "dev":
Docker creates a mount point missing from the image owned by root, and nothing
in the container could fix that afterwards.

Bash also wrote the history file only when the shell exited, which a container
stop does not always allow. Appending on every prompt keeps the session's
history even when the shell is killed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L6orjBAZq9L5t7rV5RDmXd
@nozaq
nozaq force-pushed the claude/bash-history-persistence-w8pkhc branch from 377df5e to 677f710 Compare September 12, 2026 05:50
@nozaq nozaq changed the title image(debian): add a mount point for persistent shell history image(debian): move the shell history to a mountable directory Sep 12, 2026
@nozaq
nozaq merged commit a1c8bc0 into main Sep 12, 2026
77 checks passed
@nozaq
nozaq deleted the claude/bash-history-persistence-w8pkhc branch September 12, 2026 06:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants