Skip to content

feat: zsh as the default shell - #6

Merged
vitramir merged 1 commit into
mainfrom
feat/zsh-default
Aug 7, 2026
Merged

feat: zsh as the default shell#6
vitramir merged 1 commit into
mainfrom
feat/zsh-default

Conversation

@vitramir

@vitramir vitramir commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Sandbox terminal sessions were landing in sh (dash on Ubuntu) — no history, no completion, no usable prompt.

terminal-proxy resolves a SHELL session to exec /bin/sh -lc 'PATH=/agyn/bin:$PATH exec ${SHELL:-sh}'. Nothing ever set SHELL: the kubelet doesn't, only login/su/sshd would set it from /etc/passwd, and we exec directly into the container. The image's SHELL ["/bin/bash", "-lc"] is a build-time directive that changes which shell RUN uses — it produces no runtime env var. So the :-sh branch always won.

Changes

  • Install zsh (brings zsh-common, so Ubuntu's compinit and key bindings come along).
  • ENV SHELL=/usr/bin/zsh — the only thing terminal-proxy actually reads.
  • chsh -s /usr/bin/zsh root so /etc/passwd agrees, for anything arriving via login/su.
  • Ship /root/.zshrc with history and prompt defaults. This also suppresses zsh-newuser-install, which fires on an interactive zsh when the user has no rc files — a setup wizard would otherwise greet the first sandbox terminal.
  • Append the Debian-convention emulate sh -c 'source /etc/profile' to /etc/zsh/zprofile. Ubuntu ships that file as comments only, so a login zsh never sourced /etc/profile and had no Nix. RUN zsh -lc 'nix --version && docker --version' keeps that honest at build time.

SHELL ["/bin/bash", "-lc"] is left as bash — it's build-time only and child images (devcontainer-agyn, devcontainer-nextjs-demo) inherit it for their own RUN steps. Only the misleading comment changed.

Verification

Built locally for arm64 and replayed terminal-proxy's exact command chain:

printf 'echo "ZSH_VERSION=[$ZSH_VERSION]"; echo "PATH=$PATH"\n' | docker run --rm -i <image> \
  /bin/sh -c "exec /bin/sh -lc 'PATH=/agyn/bin:\$PATH exec \${SHELL:-sh}'"
  • ZSH_VERSION=[5.9], PATH=/agyn/bin:/root/.nix-profile/bin:…, NIX_SSL_CERT_FILE intact
  • On a real PTY: prompt renders, zle and bracketed paste active, no setup wizard
  • getent passwd root/usr/bin/zsh; nix and docker work under both bash -lc and zsh -lc

Follow-up

devcontainer-agyn pins ghcr.io/agynio/devcontainer:sha-8ff09f8 and needs an ARG bump to pick this up. devcontainer-nextjs-demo tracks :latest and gets it automatically.

A terminal session execs ${SHELL:-sh}, and nothing set SHELL, so sessions
landed in dash. Publish SHELL, set root's passwd entry, and ship a .zshrc
so zsh-newuser-install does not greet the first interactive session.

/etc/zsh/zprofile is comments only on Ubuntu, so a login zsh never sourced
/etc/profile and had no Nix. The profile hook and its build-time check
close that.
@vitramir
vitramir merged commit 39f4457 into main Aug 7, 2026
3 checks passed
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.

1 participant