Installing jcode on nix/NixOS: A Practical Unofficial Guide
jcode is an open-source AI coding agent
that runs as a local server and drives your editor. It's distributed as a
single self-contained executable, which sounds simple, until you try to run it
in a pure Nix environment: no system package manager, no Nix channels, no/usr/bin/env. This post walks through installing the official Nix build from
GitHub releases, the surprising gotchas you will hit, and how to fix them.

What you need
- A machine where the Nix store is present and mounted: a
bwrap-nixsandbox,
NixOS, or any host withnixinstalled. The jcode binary is dynamically
linked against the glibc shipped in the store, so the store must be there at
runtime. curlandsha256sumfor downloading and verifying the release.- A writable
~/.local/bin(or any directory on yourPATH).
Step 1: Find the release
jcode publishes Nix builds as GitHub releases tagged nix-vX.Y.Z, for examplenix-v0.66.0. Each release carries one asset per platform:
jcode-nix-linux-x86_64
The release page shows the exact sha256 digest for every asset, so you can
verify your download:
curl -s https://api.github.com/repos/grigio/jcode/releases/tags/nix-v0.66.0 \
| jq -r '.assets[] | .name + " " + .digest'
Step 2: Download and verify
mkdir -p ~/jcode-install && cd ~/jcode-install
curl -sSL -o jcode-nix-linux-x86_64 \
https://github.com/grigio/jcode/releases/download/nix-v0.66.0/jcode-nix-linux-x86_64
sha256sum jcode-nix-linux-x86_64
Compare the output against the digest published on the release page. For
v0.66.0 it is:
2faea9dc9f560e6f9a73da360d57b3c1d32f4ccfa90bbea2352237ba43472bb3 jcode-nix-linux-x86_64
A checksum mismatch means a corrupted download or a tampered asset. Stop and
re-download.
Step 3: Confirm it is a real executable
The Nix build is a genuine ELF binary, not a script. file should show a
dynamically linked PIE with a Nix store interpreter:
file jcode-nix-linux-x86_64
# ELF 64-bit LSB pie executable, x86-64, dynamically linked,
# interpreter /nix/store/<hash>-glibc-<ver>/lib/ld-linux-x86-64.so.2
Then check the version:
chmod +x jcode-nix-linux-x86_64
./jcode-nix-linux-x86_64 --version
# jcode v0.66.0 (nix-build)
Step 4: Install
Put the binary on your PATH:
mkdir -p ~/.local/bin
cp jcode-nix-linux-x86_64 ~/.local/bin/jcode
chmod +x ~/.local/bin/jcode
~/.local/bin/jcode --version
Replacing a running jcode: "Text file busy"
If you are upgrading and a jcode server is currently running from that path, a
plain cp fails with Text file busy. Linux refuses to truncate an executable
that is mapped by a live process. Copy to a temporary name and atomically
rename it instead:
cp jcode-nix-linux-x86_64 ~/.local/bin/jcode.new
chmod +x ~/.local/bin/jcode.new
mv -f ~/.local/bin/jcode.new ~/.local/bin/jcode
The old server keeps running from its in-memory image; the new version is used
by the next server start.
Step 5: Understand jcode's layout
jcode manages its own builds under ~/.jcode/builds/:
~/.jcode/builds/
shared-server/jcode # what the server actually launches
stable/jcode # the stable build
versions/<version>/ # per-version builds from self-update
shared-server-version # version marker
stable-version # version marker
After a manual Nix install, point the shared-server and stable symlinks at
the real ELF in ~/.local/bin/jcode:
ln -sfn "$HOME/.local/bin/jcode" "$HOME/.jcode/builds/shared-server/jcode"
ln -sfn "$HOME/.local/bin/jcode" "$HOME/.jcode/builds/stable/jcode"
and keep the version markers in sync:
echo 0.66.0 > ~/.jcode/builds/shared-server-version
echo 0.66.0 > ~/.jcode/builds/stable-version
The Nix gotcha: self-update breaks the sandbox
jcode self-updates by downloading a new build into~/.jcode/builds/versions/<version>/jcode and pointing shared-server/jcode
at it. That downloaded file is not the binary. It is a wrapper shell script
whose first line is:
#!/usr/bin/env sh
In a Nix-only environment /usr/bin/env does not exist (the env binary lives
in the store under coreutils), so execve(2) fails with ENOENT and the
server never starts. Verified with strace:
execve(".../.jcode/builds/shared-server/jcode",
["...", "--provider", "auto", "serve"], ...) = -1 ENOENT
Two equivalent fixes. Do one of them.
Option A (recommended): point shared-server at the real binary
ln -sfn "$HOME/.local/bin/jcode" "$HOME/.jcode/builds/shared-server/jcode"
No wrapper, no interpreter, no breakage. This also pins the server to the
binary you installed instead of an untested self-update.
Option B: provide /usr/bin/env
Give the shebang what it wants by symlinking the store env into /usr/bin
(only possible when /usr/bin is writable):
ln -sf "$(dirname "$(readlink -f "$HOME/.local/bin/env")")/env" /usr/bin/env
# e.g. /nix/store/<hash>-coreutils-9.10/bin/env
Run the server
~/.jcode/builds/shared-server/jcode --provider auto serve
or directly:
~/.local/bin/jcode --provider auto serve
Notes:
- Only one server instance may run per runtime dir. A second launch fails
withError: Another jcode server process is already running for runtime dir /tmp/<...>/jcode-<uid>. To restart, kill the existing PID first, then start
again. - Verify the daemon is alive with
jcode --versionor by checking the runtime
dir (/tmp/<...>/jcode-<uid>/) forjcode.sockandjcode-debug.sock.
Upgrading to a new release
The process is the same every time: download the new jcode-nix-linux-x86_64
asset, verify its digest, mv it over ~/.local/bin/jcode, re-run the
symlink command if anything changed, update the version markers, and restart
the server. The old binary can be kept as a rollback:
cp ~/.local/bin/jcode ~/.local/bin/jcode.v0.65.0.bak
Checklist
- [ ]
sha256summatches the digest on the release page - [ ]
filereports an ELF, not a shell script - [ ]
~/.local/bin/jcode --versionprintsjcode vX.Y.Z (nix-build) - [ ]
shared-server/jcodeandstable/jcodeare symlinks to the ELF - [ ] version markers match the installed version
- [ ] the server starts and binds its socket
Conclusion
Installing jcode on Nix is straightforward once you know the rules: download
the Nix-built ELF, verify it, drop it on your PATH, and keep jcode's own
symlinks pointed at the real binary instead of a self-update wrapper. The/usr/bin/env trap is the one thing that will bite you, and Option A above
makes it a non-issue forever.