Repository navigation
Docker Image
The browser version also ships as a Docker image, so it runs on a server or any machine with Docker, without
installing Node or the program. The image is ghcr.io/shibbirweb/nibtree (and shibbirweb/nibtree on Docker
Hub). This page explains how it is built, tested and published. The npm side is in
Browser Version Packages.
docker run --rm -p 4410:4410 \
-v "$PWD:/workspace" \
-v nibtree-home:/home/nibtree \
-e NIBTREE_TOKEN=pick-a-secret \
ghcr.io/shibbirweb/nibtreeThen open http://localhost:4410/?token=pick-a-secret. Without NIBTREE_TOKEN the server makes a random token
and prints the address in the container's log (docker logs).
-
Port 4410 is the image's own port. The npm version starts at 4300 and never moves on to 4410, so both can
run on one machine. With another outside port (
-p 9000:4410), setNIBTREE_PUBLIC_URL=http://localhost:9000so the printed address is right. -
/workspaceis the folder the app opens. Pass another path inside the container as the command to open that one instead. -
/home/nibtreeholds the settings (~/.nibtree-browser) and git's config. A named volume keeps them. -
Your identity and keys: mount them read-only, for example
-v ~/.gitconfig:/home/nibtree/.gitconfig:ro -v ~/.ssh:/home/nibtree/.ssh:ro. -
File owners: on Linux add
--user "$(id -u):$(id -g)", so files the app writes belong to you.
docker/compose.yaml runs the same container with docker compose up -d. It differs from the docker run line
on purpose: it listens on 127.0.0.1 only, so other machines cannot reach it until you change that line, and it
restarts with Docker. Every choice is a variable with a default, so the file works unchanged:
NIBTREE_TOKEN, NIBTREE_TAG (default next), NIBTREE_WORKSPACE (default: the file's folder),
NIBTREE_OUTSIDE_PORT (default 4410, which also sets NIBTREE_PUBLIC_URL) and NIBTREE_USER. NIBTREE_IMAGE
replaces the whole image name, which CI uses to start the image it just built.
flowchart LR
P["@nibtree/linux-x64 and linux-arm64"] -->|"docker-context.ts"| C["context: amd64/ and arm64/"]
C -->|"docker/Dockerfile"| I["debian:bookworm-slim + git"]
I --> R["ghcr.io and Docker Hub"]
docker/Dockerfile starts from debian:bookworm-slim and adds what the app needs to work like it does in a
terminal: git (every write goes through the git command), git-lfs, openssh-client for SSH remotes,
gnupg for signed commits, ca-certificates for HTTPS and bash for the built-in terminal. tini is the
first process: it passes docker stop on to the server and cleans up the terminals' shells.
The program and its page are not compiled in Docker. bun scripts/docker-context.ts unpacks the Linux npm
packages that CI already built and tested into amd64/ and arm64/, and the Dockerfile copies the one for
its architecture. So the image runs the very same files as npx nibtree on Linux.
The image sets NIBTREE_HOST=0.0.0.0, NIBTREE_PORT=4410 and NIBTREE_NO_OPEN=1. The program reads these
NIBTREE_* variables as defaults for its options, and options on the command line still win. It runs as the
user nibtree (uid 1000), and the system git config marks every folder as safe, because a mounted folder
usually belongs to the host's user.
.github/workflows/browser.yml has a docker job on ubuntu-22.04 (amd64) and ubuntu-22.04-arm (arm64).
Each builds the image from its platform's package and runs bun scripts/browser-smoke.ts --docker <image>:
-
--versionthrough the image's entry point; -
git,git-lfs,ssh,gpgandbashare in the image; - a container as the current user, with a new repository on
/workspace, a new home folder on/home/nibtree, a random outside port andNIBTREE_PUBLIC_URL; the printed address must be that one; - the same checks as the npm smoke test: the token, the page, the socket,
get_status, a commit through the git command, and settings in~/.nibtree-browser; -
docker stopmust end it in under 8 seconds, which showstinipassed the signal on.
Then the job starts the same image through docker/compose.yaml: docker compose config checks the file,
up --wait waits for the health check, and the page must load with the token.
For a release (publish: true), docker-publish builds both architectures with Buildx and pushes them as one
image. QEMU only runs the image's apt-get for arm64: the programs are already built. The tags come from
scripts/dockerImage.ts: the exact version, plus next for a beta or latest for a stable release, the same as
on npm.
-
ghcr.io uses the workflow's built-in token, so it needs no secret, but every caller must grant
packages: write(release.yml,beta-publish.ymlandstable-publish.ymldo). The first push creates the package as private: open it under the account's Packages, Package settings, and make it public once. -
Docker Hub needs the secrets
DOCKERHUB_USERNAMEandDOCKERHUB_TOKEN(an access token with read and write). Without them the release fails, so the image never quietly skips a registry.
After the push, verify-docker pulls the version and its moving tag from both registries, checks both
architectures are there and runs --version. See Browser Release Checks.
| File | What it does |
|---|---|
docker/Dockerfile |
the image |
scripts/docker-context.ts |
the build context from the Linux packages |
scripts/dockerImage.ts, scripts/docker-tags.ts
|
architectures and tags (tested) |
scripts/browser-smoke.ts |
the smoke test, --docker mode |
src-tauri/browser/src/options.rs |
the NIBTREE_* defaults, --public-url and port 4410 |
From the npm packages, not a build in Docker. Compiling Rust for arm64 under QEMU takes far longer, and two builds could differ. Reusing the tested packages keeps one program for both.
Debian, not Alpine. The program links glibc, and Debian's git is the one most people run.
Environment variables as defaults. They let the image stay a plain nibtree-browser /workspace, so any
other folder or option can be passed as the command without repeating the image's settings.
These pages are generated from docs/wiki. To correct something, open a pull request against the source file; edits made here are overwritten.
Using Nibtree
- Getting Started
- Welcome Screen
- Workspaces
- Files Panel
- File Operations
- File Icons
- Rounded Panels
- Editor and Tabs
- Pin Reorder and Wrap Tabs
- Single Tab Title
- Split Editor
- New File and Unsaved Changes
- Unload Hidden Tabs
- Editing Code
- Code Appearance
- Markdown Editor
- Image and PDF Preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and Replace
- Changes and Commits
- Sign In Prompts
- Commit Box Layout
- Repository Actions
- Commit Options
- Ignoring Files
- Diffs
- Inline Diffs
- Staging View
- Revert a Change
- Resolving Conflicts
- Merge Tool
- Blame
- History and Log
- Merge Commits
- Branches and Tags
- Branches Popup
- Remotes
- Stashes
- Shelf
- Git Menu
- Git Dialogs
- Interactive Rebase
- Worktrees
- AI Agents
- AI Tasks
- AI Usage
- Keep Computer Awake
- Submodules
- Git LFS
- GitHub
- Git Console
- Terminal
- Terminal Features
- Scripts
- Navigation
- Menus
- Settings
- Settings Search
- Color Themes
- Settings Terminal and Automation
- Settings Files
- Updates
- Status Bar and Help
- Notifications
- Memory Use
- Clear Cache
- MCP and CLI
- Git Mergetool
- Keyboard Shortcuts
- Keyboard Shortcuts Editor
- Troubleshooting
- Troubleshooting Git
Developers
- Developer Guide
- Architecture
- Project Layout
- Backend
- How Folder Watching Works
- Folder Watching Bugs We Fixed
- How Workspace Files Work
- How the Preview Scheme Works
- Backend Services
- How Terminal Features Work
- Terminal Bugs We Fixed
- Frontend
- How the Rich Markdown Editor Works
- Rich Markdown Bugs We Fixed
- Git Menu Links
- Menu Keys and Routing
- Frontend Modules
- Blame Bugs We Fixed
- Project Color And Title Bar
- One Diff View
- Diff Bugs We Fixed
- Merge Tool Layouts
- Merge Tool Bugs We Fixed
- Color Theme Bugs We Fixed
- How the Connected Diff Works
- Rounded Panels Bugs We Fixed
- Update Bugs We Fixed
- Status Bar Bugs We Fixed
- MCP and CLI Bugs We Fixed
- Repository Actions Bugs We Fixed
- How the Changes Tab Works
- CodeMirror Patterns
- How the Cursor Works
- How Editor Features Work
- How the Path Bar Works
- Commands and Events
- Settings Reference
- Commands Workspace and Changes
- Commands Branches and Remotes
- Commands History and Extras
- Commands App and Tools
- Testing
- Test Suites
- Debugging
- Debugging with MCP
- Measuring Setting Memory
- How Terminal Memory Is Measured
- How Markdown Diagrams Work
- Releases and CI
- Publishing the Wiki
- Versioning and Changelog
- Browser Version Packages
- Browser Release Checks
- Docker Image
- Platforms and Signing
- Windows Support
- Windows Bugs We Fixed
- Windows MCP and CLI
- Docs and Screenshots
- UI Library
- UI Library MCP
- UI Library Bugs
- UI Library Migration
- Contributing
How features work
- Welcome screen
- Workspaces and folders
- Files panel
- File operations
- File icons
- Editor and tabs
- Pin, reorder and wrap tabs
- Single tab title
- Split editor
- New File and unsaved changes
- Unload hidden tabs
- Editing code
- Code appearance
- Markdown editor
- Image and PDF preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and replace
- Changes and commits
- Sign-in prompts
- Commit box layout
- Repository actions
- Commit options
- Ignoring files
- Diffs
- Inline diffs
- Staging view
- Revert a change
- Resolving conflicts
- Merge tool
- Blame
- History and log
- Merge commits
- Commit tabs
- Branches and tags
- Branches popup
- Remotes
- Stashes
- Shelf
- Git menu
- Git dialogs
- Interactive rebase
- Worktrees
- AI agents
- AI usage
- Keep computer awake
- AI tasks
- Submodules
- Git LFS
- GitHub account
- Git Console
- Back and Forward
- Integrated terminal
- Scripts tool window
- Menu bar
- Settings
- Search in Settings
- Color themes
- Rounded panels
- Updates and release channels
- Status bar and help links
- Notifications
- Memory readout and memory log
- Clear Cache
- MCP server and command line tool
- git mergetool mode