Sign inSign up

cartagodocker/nodebun

By cartagodocker

β€’Updated about 4 hours ago

Image for charging bun, fnm, node, npm and zsh.

Image
Integration & delivery
Developer tools
Content management system
1

1.5K

cartagodocker/nodebun repository overview

⁠🟒 cartagodocker/nodebun

Node, npm, bun, and fnm on top of cartagodocker/zsh⁠ (Ubuntu 24.04 LTS + zsh + Oh My Zsh + Powerlevel10k).

Same layout as the zsh README⁠. This page only expands what NodeBun adds. Shell, sudo, SSH, fonts, and the daily CLI are inherited β€” details live there.

Tags are exact. There is no latest / stable. Pin the matrix you need.

FROM cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2

Pin cartagodocker/zsh:v2.0.0. There is no latest.


β πŸ“¦ What's in the image

PieceNotes
🐧OSUbuntu 24.04 LTS β€” from cartagodocker/zsh⁠
πŸ“Œzsh pincartagodocker/zsh:v2.0.0
🐚Interactive shellzsh + Oh My Zsh + Powerlevel10k β€” CMD ["/usr/bin/zsh"], no ENTRYPOINT (zsh 2.0.0)
πŸ’»Other shellsbash and sh (dash) β€” inherited
🟒Node / npm / bun / fnmInstalled at build; on PATH for every uid and every shell
πŸ“‚Listing / pagerInherited from zsh β€” full list in Utilities⁠
🧰Daily CLIInherited from zsh (not reinstalled). Full inventory: Utilities⁠
πŸ“–Cataloguedockernodebun --help β€” zsh catalogue, then NodeBun extras (see Catalogue CLI⁠)
🌐NetworkInherited: curl, wget, git, openssh-client (no sshd), ca-certificates
πŸ”sudoInherited (NOPASSWD + sudo-password). Not overwritten when the base already has it
🌍LocaleInherited: LANG=C.UTF-8 · LC_ALL=C.UTF-8
🧱Build SHELL["/bin/sh", "-c"] β€” same as zsh

No openssh-server, no ENTRYPOINT: docker run / Compose command: is the process (node --version works on zsh 2.0.0).

Not in this image (same as zsh): gcc/g++, python3, neovim, git-lfs, rclone, locales, man-db, nmap, no sshd, no Docker. NodeBun does not add a second CLI kit β€” only runtimes: fnm, one Node (matrix), npm, two bun binaries (AVX2 + baseline), libatomic1. Compilers / Docker client go in a further child. Drive this container with host docker.

sudo / dockerzsh / add_text_to_* come from cartagodocker/zsh:v2.0.0. Not reinstalled; no fallback for older zsh tags.


⁠🧰 Utilities

From zsh (not reinstalled): zsh README β€” Utilities⁠ (name β†’ docs). Same aliases (lsβ†’eza in interactive zsh/bash/sh). In-container: dockerzsh --help or dockernodebun --listing.

This image adds the tables below. Usage: Scripts for child images⁠. zsh helpers: zsh README β€” Scripts⁠ or dockernodebun --zsh-helpers.

⁠Runtimes (NodeBun)

On PATH (/usr/local/bin) for every uid and every shell. libatomic1 is for bun. /usr/local/bin/bun is a bash wrapper (AVX2 vs baseline from /proc/cpuinfo; chatter if BUN_WRAPPER_DEBUG=1). bunx is the same wrapper (bunx pkg β†’ bun x pkg). fnm use is this shell only (FNM_DIR=/usr/share/fnm/store, 777).

ToolDocs
nodeNode.js⁠
npm / npxnpm⁠ · npx⁠
bun / bunxbun⁠ · bunx⁠
fnmfnm⁠
⁠NodeBun helpers (ours)
CommandDocs
in-bashScripts⁠
in-shScripts⁠
only-in-containerScripts⁠
skip-if-containerScripts⁠
dockernodebunCatalogue⁠

β πŸ“ Image size

docker images is uncompressed. Hub pull is gzip layers (smaller). On top of cartagodocker/zsh:v2.0.0.

Uncompressed (disk / docker images)Compressed (Hub pull / download)
zsh v2.0.0 (parent tree)~240–260β€―MB~110–125β€―MB
Node official linux-x64 (26.3.1 / 22.21.1)~90–120β€―MB unpacked~32β€―MB / ~29β€―MB .tar.xz
bun AVX2 zip + baseline zip~2 Γ— 90–110β€―MB unpacked~34β€―MB + ~34β€―MB zips
fnm~8β€―MB~3β€―MB zip
libatomic1~0.04β€―MBtiny
Whole tag (local 2.0.2 on Hub zsh 2.0.0, Node 26)~695β€―MBHub layers (not listed for every matrix)

Two bun builds on purpose: /usr/local/bin/bun is a bash wrapper (bun_wrapper.zsh β€” historical .zsh name, #!/bin/bash) that picks AVX2 vs baseline from /proc/cpuinfo. That doubles bun on disk. Silent unless BUN_WRAPPER_DEBUG=1. One Node is baked (fnm install of the matrix); extra fnm install at runtime grows FNM_DIR.

Disk β‰ˆ zsh 2.0.0 + Node + two bun + fnm: local smoke ~695β€―MB uncompressed (Node 26). Hub NodeBun 2.0.0 on zsh 1.0.5 was ~642β€―MB.


⁠🏷️ Runtime matrices

Defined in .github/matrices.yml⁠:

MatrixNodeBunnpmfnmStatus
n22.21.1_b1.4.222.21.11.4.210.9.41.38.1βœ… Node 22 LTS
n26.3.1_b1.4.226.3.11.4.212.0.11.39.0βœ… Node 26 current
v{X.Y.Z}_n{node MAJOR.MINOR.PATCH}_b{bun MAJOR.MINOR.PATCH}

Examples: v2.0.2_n22.21.1_b1.4.2, v2.0.2_n26.3.1_b1.4.2.

X.Y.Z counts repo corrections (Dockerfile, wrapper, docs) and is published on all active matrices in parallel. Full policy: VERSIONING.md⁠.


⁠▢️ How to run it

Two different jobs. Mix them up and it looks like β€œeza is broken”.

Node / bun / npm / fnm are baked at docker build. docker run does not download them. On PATH (/usr/local/bin) for any uid and any shell.

ToolAt buildAt start
Nodefnm install β†’ FNM_DIR=/usr/share/fnm/store/usr/local/bin/node
npmnpm install -g npm@…/usr/local/bin/npm
bun / bunxAVX2 + baseline zips; wrapper reads /proc/cpuinfo; bunx β†’ bun x/usr/local/bin/bun, /usr/local/bin/bunx
fnmrelease zip/usr/local/bin/fnm

zsh extras (p10k) load from ~/.zshrc and need a TTY. Interactive bash/sh also get ls→eza. node / npm / bun do not need a TTY.

⁠πŸ–₯️ Interactive prompt (eza, bat, p10k)

Needs a TTY (-it or exec -it).

# Hub: IMG=cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2
IMG=nodebun-local:dev
docker run --rm -it --user 1000:1000 -w /home/ubuntu \
  -v "$HOME/.ssh:/$USER/.ssh:ro" \
  -v "$HOME/.gitconfig:/$USER/.gitconfig:ro" \
  "$IMG"
docker run --rm -it "$IMG"   # root, no bind

ls→eza. bat works. p10k draws the prompt. node/npm/bun work in every shell. Inside zsh: bash, sh, exit. Interactive bash/sh alias ls→eza; no p10k.

⁠🧊 Keep-alive (Compose)

The container stays up without a shell. Typical:

services:
  app:
    image: cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2
    command: ["tail", "-f", "/dev/null"]
    user: "1000:1000"
    working_dir: /work
    volumes:
      - .:/work

That process is tail, not zsh, and not a TTY. No prompt, no eza aliases, no p10k. Attach when you want the shell:

docker compose exec app zsh          # prompt + eza + bat + p10k
docker compose exec app bash         # bash; node/npm/bun still on PATH
docker compose exec app bun install
docker run --rm cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2 node --version

docker run --rm image bash and docker run --rm image node --version work because there is no ENTRYPOINT swallowing the command (cartagodocker/zsh:v2.0.0).


β πŸ” sudo

Inherited from zsh. Global files (/usr/share/globally/.zshrc) are 644 root:root β€” uid 1000 cannot overwrite them. Use sudo or add_text_to_zshrc (NOPASSWD). node/npm/bun do not need sudo.

docker run --rm -it --user 1000:1000 cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2
# inside:
sudo -n id                 # default: no password
sudo-password              # prompt; afterwards sudo asks for it
sudo-password 'secret'     # from arg (visible in ps)
sudo-nopasswd              # back to NOPASSWD

Password at start (not baked into the image):

docker run --rm -it -e SUDO_PASSWORD=secret cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2

Compose user: "1000:1000" drops extra groups, so the sudoers rule is ALL, not %sudo.


⁠🎨 Fonts and icons

Same contract as cartagodocker/zsh⁠. The container only emits Unicode. The host terminal draws glyphs.

WhatNeeds on the host
πŸ”·Powerlevel10k separators, git icons, eza file iconsA Nerd Font⁠ (CaskaydiaCove / Cascadia Code NF)
🐳Prompt whale (os_icon)An emoji font β€” Segoe UI Emoji (Windows), Apple Color Emoji (macOS), Noto Color Emoji (many Linux desktops). Some Linux terminals have none β†’ tofu. Same Unicode everywhere; the font is local.

VS Code:

"terminal.integrated.fontFamily": "'Cascadia Code NF', 'CaskaydiaCove Nerd Font', Consolas, monospace"

Without a Nerd Font you get boxes on powerline / ls icons β€” not an image bug.


⁠🧩 Scripts for child images

⁠From zsh (inherited)

add_text_to_zshrc, add_text_to_p10k, share_config_globally, sudo-password, sudo-nopasswd β€” same CLI as zsh⁠. Write /usr/share/globally/...; sudo if needed. Full text: dockerzsh --helpers or dockernodebun --zsh-helpers.

⁠NodeBun only

Most people never type these (bash pepe.sh is enough). Same Makefile on host and in-container. Detect /.dockerenv or IS_INTO_CONTAINER=true.

⁠in-bash

bash --login so /etc/profile.d/nodebun.sh loads (BUN_* / FNM_*). For snippets that are not zsh-safe (set -o pipefail, [[ ]], bash arrays). No args: interactive bash.

in-bash
in-bash -c 'set -o pipefail; npm test | tee log'
in-bash script.sh
⁠in-sh

POSIX sh -l (dash + login env). Same idea as in-bash for portable snippets.

in-sh
in-sh -c 'case $1 in *.sh) echo posix ;; esac'
in-sh script.sh
⁠only-in-container

Run CMD only inside this container. No args: exit 0 inside, 1 outside (predicate). Outside with a command: error, exit 1.

only-in-container                  # 0 inside, 1 on the host
only-in-container bun run test     # skip on the host
⁠skip-if-container

No-op inside (exit 0); run CMD on the host. For host-only tools (adb, docker, osascript). No args on the host: error.

skip-if-container adb start-server

Catalogue dump of these four: dockernodebun --helpers.


β πŸ”‘ SSH (client only β€” no sshd)

Inherited from zsh. Bind host ~/.ssh and type ssh / git. Root or any uid β€” one volume. Git author is not the SSH key: bind ~/.gitconfig (or GIT_AUTHOR_NAME + GIT_AUTHOR_EMAIL). Full contract: zsh README β€” SSH⁠.

services:
  app:
    image: cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2
    volumes:
      - ~/.ssh:/${USER}/.ssh:ro
      - ~/.gitconfig:/${USER}/.gitconfig:ro
docker run --rm -it \
  -v "$HOME/.ssh:/$USER/.ssh:ro" \
  -v "$HOME/.gitconfig:/$USER/.gitconfig:ro" \
  cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2
# inside: ssh [email protected]
#         git commit   # author = host user.name, not ubuntu@id

${USER} is the host name. Docker needs an absolute target (/${USER}/.ssh), not ${USER}/.ssh. Not SSH-into-the-container β€” attach with docker exec -it … zsh.


⁠🌍 Environment

VariableMeaning
NODE_DEFAULT_VERSIONMatrix Node
FNM_HOME / FNM_DIR / FNM_BIN/usr/share/fnm, store, bin
BUN_HOME / BUN_INSTALL/usr/share/bun
IS_INTO_CONTAINERtrue
VERSIONConvenience X.Y.Z. A child FROM nodebun that sets its own VERSION overwrites this.
NODEBUN_IMAGE_VERSIONThis NodeBun tag. dockernodebun --version uses it, not VERSION.
ZSH_IMAGE_VERSIONParent zsh tag (cartagodocker/zsh). dockerzsh --version uses it.
LANG / LC_ALLInherited C.UTF-8

Login shells (su -, bash -l) drop Docker ENV. /etc/profile.d/nodebun.sh re-exports NodeBun vars (also from /etc/zsh/zprofile β€” Ubuntu login zsh does not source /etc/profile). Same script on every matrix. This image’s Node / X.Y.Z live in /usr/share/nodebun/build.env (from ARG at build) and fill only when ENV is unset. Interactive zsh still runs fnm env from .zshrc.

FNM_DIR is 777 (same as bun): any uid can fnm install / fnm use. Homes and /etc/skel get ~/.local/share/fnm β†’ that store, plus ~/.local/state (fnm multishells) owned by that user so fnm env works as uid 1000.

fnm install 20.19.0
fnm use 20.19.0    # this shell only; a new shell is back on the matrix

β πŸ“– Catalogue CLI (dockernodebun)

Inside a running container dockernodebun is the zsh catalogue first (dockerzsh --help), then NodeBun extras. Not the host docker CLI.

docker run --rm cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2 dockernodebun --help
docker compose exec app dockernodebun --help

The full dump is long on purpose. Filter by section. NodeBun ids stay here; zsh ids go to dockerzsh.

dockernodebun --sections | -s    # zsh ids + NodeBun ids
dockernodebun --runtimes         # node / npm / bun / fnm
dockernodebun --helpers          # in-bash, in-sh, only-in-container, skip-if-container
dockernodebun --zsh-helpers      # add_text_to_zshrc, add_text_to_p10k, share_config_globally, sudo-password, sudo-nopasswd, git-from-host (zsh)
dockernodebun --env              # NODE_DEFAULT_VERSION, FNM_DIR, BUN_*, IS_INTO_CONTAINER
dockernodebun --shells           # zsh section (forwards)
dockernodebun shells env         # mix zsh + NodeBun sections
dockernodebun --version | -v     # image identity (NODEBUN_IMAGE_VERSION)
dockernodebun --list | -l        # tool names (this image + zsh)
dockernodebun --list --version | -l -v
dockernodebun runtimes --version | runtimes -v
dockernodebun node --version | node -v
IdWhereSection
nodebunthis imageIntro (PATH at build, no ENTRYPOINT, TTY vs keep-alive)
runtimesthis imagenode, npm, npx, bun, fnm
helpersthis imagein-bash, in-sh, only-in-container, skip-if-container
envthis imageNODE_DEFAULT_VERSION, FNM_*, BUN_*, IS_INTO_CONTAINER
shells, listing, edit, archives, network, system, extras, helpers, fontszsh baseSame ids as dockerzsh --sections⁠ β€” full tool list in zsh README β€” Utilities⁠
zsh-helperszsh baseadd_text_to_zshrc, add_text_to_p10k, share_config_globally, sudo-password, sudo-nopasswd, git-from-host, dockerzsh

--helpers on this CLI is NodeBun. zsh helpers: dockernodebun --zsh-helpers or dockerzsh --helpers. Unknown ids exit 2.


⁠🧱 Child image

FROM cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install

Keep SHELL ["/bin/sh", "-c"] if you override it (cartagodocker/zsh:v2.0.0 already sets it).


β πŸš€ Build and publish

docker build \
    --build-arg VERSION=2.0.2 \
    --build-arg NODE_DEFAULT_VERSION=26.3.1 \
    --build-arg BUN_VERSION=1.4.2 \
    --build-arg FNM_VERSION=1.39.0 \
    --build-arg NPM_VERSION=12.0.1 \
    -t cartagodocker/nodebun:v2.0.2_n26.3.1_b1.4.2 \
    -f ./Dockerfile ./

The GitHub workflow fills those from .github/matrices.yml.

Note: when bumping ARG VERSION in the Dockerfile, also update the ENV VERSION=… and ENV NODEBUN_IMAGE_VERSION=… literals that follow the multiline ENV block. The workflow only injects the ARG, not the literal ENVs β€” they exist because cartagodocker/zsh:v2.0.0 already exports ENV VERSION=2.0.0 and Docker would otherwise shadow the ARG with that inherited value. See [2.0.2] in CHANGELOG.md⁠.

GitHub Actions (secrets DOCKERHUB_USERNAME, DOCKERHUB_PASSWORD; variable DOCKERHUB_REPO):

TriggerWhat happens
Git tag v{X.Y.Z}One Hub tag per active matrix if new (skip existing) + GitHub Release for v{X.Y.Z} (replaced if it exists) β€” docker-hub-update.yml⁠
Git tag v{X.Y.Z}_n…_b…That matrix only. Skip Hub if the tag exists. GitHub Release for that matrix tag (replaced if it exists; not latest). No Hub :latest.
Push to main that changes README.mdHub long description (full_description ≀ ~25β€―000 chars) β€” update-dockerhub-description.yml⁠
git tag v2.0.1
git push origin main v2.0.1

See CHANGELOG.md⁠ and VERSIONING.md⁠.

Tag summary

Content type

Image

Digest

sha256:a536445c3…

Size

232.4 MB

Last updated

about 4 hours ago

docker pull cartagodocker/nodebun:v2.0.2_n22.21.1_b1.4.2