Nushell 0.117.0 shipped on 9 November 2026, and it is the most substantial release the project has shipped in a while. The Rust-written shell now has roughly 41,000 GitHub stars, an MIT licence, and a feature list that finally delivers on the original promise of the language: *stop parsing text, start piping data*.
Most of the headline work in this release came from Nushell maintainer @fdncred, and the theme running through all of it is the same — let programs exchange typed records instead of flattened strings.

*Figure: AI-generated illustration. Real screenshots were unavailable for the release banner at time of writing.*
## The problem Nushell has always had
Nushell is a structured shell. Its built-in commands do not return lines of text — they return tables of records, which you can filter, sort, and reshape with real operators:
“`nu
ls | where size > 1kb | sort-by modified | select name size
“`
The trouble starts the moment you leave the shell. Pipe `ls` into `grep`, run `docker ps`, or call `git status`, and you are back to squinting at human-formatted output and parsing it with `awk` or `sed`. That gap between Nushell’s internal data model and the messy outside world is where most of the friction — and most of the shell scripting — lives.
## Less parsing, more piping: the @structured attribute
The headline feature of 0.117.0 closes that gap directly. A new `@structured` attribute lets you teach an external command how to produce structured data when it is piped into Nushell, without changing how it looks when you run it on its own:
“`nu
@structured [ –porcelain ] {
lines | parse –regex ‘^(?
} extern “git status” [] git status
git status | where status =~ M | get file
“`
Read that carefully. Running `git status` on its own still gives you Git’s familiar human-readable output. But as soon as you pipe it into another Nushell command, the shell transparently adds `–porcelain`, parses the response, and hands you records with named fields. `where status =~ M` now filters on a typed column instead of a `grep` pattern, and `get file` extracts the filenames.
This is the single most consequential change in the release. It means a huge amount of existing shell scripting — the `grep | awk | cut` chains that people write for `docker`, `kubectl`, `gh`, `systemctl` — can now be written as ordinary typed pipelines.

*Figure: AI-generated illustration of the structured-data pipeline concept. Real screenshots were unavailable at time of writing.*
## The REPL is one step ahead
Nushell 0.117.0 also adds a live status line beneath your input while you are still typing it. Before you press Enter, the shell can show you:
– The first parse or type error in what you are writing
– The command signature, with the current parameter emphasised
– The output type of the pipeline you are building
– A live preview of the result, for expressions built entirely from constant commands
Type `”a,b” | split row “,”` and you get a small `= [a, b]` sneak peek before you even hit Enter. It is not doing the work for you, but it is close. You can control which parts appear through `$env.config.status_line` with separate toggles for `enable`, `errors`, `signature`, `type`, and `preview`.
One nice detail: `Ctrl+C` now interrupts very large math reductions. `1..10000000000 | math sum` reports `Operation interrupted` instead of running forever.
## Completions you did not have to write
The new `generate completions` command produces Nushell `extern` definitions and completers from sources you probably already have:
– carapace specifications
– fish completions
– zsh completions
– man pages
– `–help` output
“`nu
generate completions git
| save ($nu.user-autoload-dirs | first | path join git-completions.nu)
“`
Save that module into an autoload directory and Tab starts completing Git subcommands, branch names, remotes, and tags. The source precedence is carapace, then fish, then zsh, then man, then help — and `–source all` merges them. For tools with native Cobra completions, such as `gh`, `docker`, and `kubectl`, Nushell can pick those up too.

*Figure: Official Nushell demo animation frame, from the project repository.*
## Record commands that skip the transpose round-trip
Four new commands work directly on records without converting them to tables and back:
– `record where` — filter fields by value or key
– `record each` — transform keys and values in one pass
– `record walk` — walk nested data and redact by cell path
– `record apply` — apply field-specific conversions
The `record walk` case is the one worth stealing immediately. Redacting secrets out of nested configuration data no longer needs a hand-rolled recursive function:
“`nu
{ db: { host: prod, password: hunter2 }, api: { token: abc } }
| record walk {
|value, path|
if ($path | to text) =~ ‘(?i)secret|token|password’ {
‘***’
} else {
$value
}
}
“`
Both `password` and `token` come back as `***`, while `host` stays `prod`. `record apply` does the same trick for type coercion:
“`nu
{ age: ’42’, email: ‘A@B.COM’ }
| record apply { age: { into int }, email: { str lowercase } }
“`
These commands also accept tables and stream one record per row, so `record apply` works as a column-wise type converter.

*Figure: Official Nushell screenshot — `ls` output is already structured data, not text.*
## Dotenv support, finally
The `.env` file sitting in your project can now be loaded without a homemade parser:
“`nu
open .env | load-env
“`
Supported formats include `.env`, `.env.local`, and `prod.env`. Quoted values, multiline values, comments, optional `export`, and variable expansion all work — and crucially, nothing in the file is executed as a command. Environment conversions also run when variables are loaded, so a string `PATH` becomes a list without the usual conversion dance.
## Reusable type aliases
If you have ever copy-pasted the same large record type into three command signatures, 0.117.0 lets you name it instead:
“`nu
type Point = record
type Polygon = list
def first-x [ poly: Polygon ]: nothing -> float { $poly.0.x }
“`
Aliases can refer to other aliases, including inside generic types, and can be exported from modules with `export type`. Custom types, including those from plugins, now resolve correctly in the Nushell LSP.
## What breaks, and how to migrate
This release has real breaking changes. If you already use Nushell in scripts or config, these are the ones that will bite:
**`type` is now a reserved keyword.** Any command, alias, or `extern` you defined with that name needs renaming. To call an external command named `type`, use `^type`; to look up a command inside Nushell, use `which`.
**`ps` has a new column layout.** `ps` now lists every process on every platform, including macOS and Windows. `user` is now included in plain `ps`, `virtual` is only available with `ps -l`, and `process_threads` has been renamed to `threads`. The `working` and `paged` columns are gone — use `private` for memory used exclusively by a process. Because new null values appear, drop them before sorting:
“`nu
ps | compact cpu | sort-by cpu | last 3
“`
**Keybinding and menu lists are replaced, not appended.** Assigning `$env.config.keybindings = […]` or `$env.config.menus = […]` now replaces the whole list, including Nushell’s defaults and the keys that open them (Tab completion, Ctrl+Space, F1 help, Ctrl+X and Ctrl+Z paging, the Ctrl+R history menu). Use `++=` to append instead:
“`nu
$env.config.keybindings = (config default).keybindings
$env.config.keybindings ++= [
{ name: my_binding, modifier: control, keycode: char_t, mode: emacs, event: { send: clearscreen } }
]
“`
**`group-by –to-table` keeps original key types.** Group keys are no longer converted to display strings, so `1 MB` and `1.001 MB` become separate groups instead of collapsing into one. Scripts that assumed string keys need updating.
**Operator precedence changed.** `not` now applies to the whole comparison: `not $a == $b` means `not ($a == $b)`. Previously it applied only to the next value.
**Module directories with a dot keep their full name.** `use a.b` creates module `a.b` (commands like `a.b foo`) instead of `a`.
## Performance work
Nushell now parses real scripts about **1.33x faster**, which shortens startup and script loading. On Apple silicon Macs, it uses about **3 MiB less memory** per process. Measured startup with the default config improved from ~15.70 ms to ~13.10 ms to first prompt, and instructions retired by the first prompt dropped from 149.3M to 119.8M. Closures that call `each`, `where`, or `update` run much faster too.
## Installing 0.117.0
Pre-built binaries are published for Linux, macOS, and Windows on the GitHub releases page. On Debian and Ubuntu, the project publishes its own apt repository:
“`sh
wget -qO- https://apt.fury.io/nushell/gpg.key | sudo gpg –dearmor -o /etc/apt/keyrings/fury-nushell.gpg
echo “deb [signed-by=/etc/apt/keyrings/fury-nushell.gpg] https://apt.fury.io/nushell/ /”
| sudo tee /etc/apt/sources.list.d/fury-nushell.list
sudo apt update
sudo apt install nushell
“`
Fedora, Rocky Linux, and RHEL use a yum repository at `https://yum.fury.io/nushell/`. Alpine adds `https://alpine.fury.io/nushell/` to `/etc/apk/repositories`. Homebrew users can run `brew install nushell`, and there is an `npm install -g nushell` route, though npm builds do not include plugins.
Container images are published to GitHub Container Registry for Alpine and Debian:
“`sh
docker run -it –rm ghcr.io/nushell/nushell:latest-alpine
“`
Building from source needs a Rust toolchain (1.96.1 or later), a C compiler, and `pkg-config`. Since Nushell 0.105, `rustls` replaces OpenSSL by default, so OpenSSL development headers are only needed for the `native-tls` feature.

*Figure: Official Nushell screenshot — `http get` returns a structured response you can query directly.*
## Verdict
Nushell 0.117.0 is the release that makes the structured shell argument genuinely compelling. Before, adopting Nushell meant giving up `grep`, `awk`, and every other text-processing tool you relied on. With `@structured`, `generate completions`, and the record commands, the “outside world” of untyped programs now feeds into the typed pipeline instead of the other way around.
The breaking changes are real, particularly the `type` keyword reservation and the keybinding replacement semantics. If you have an existing Nushell config, read the release notes before upgrading, and back it up.
The project is at [github.com/nushell/nushell](https://github.com/nushell/nushell), MIT licensed, roughly 41,000 stars, with the full release notes at [nushell.sh/blog](https://www.nushell.sh/blog/2026-11-09-nushell_v0_117_0.html).

*Figure: Official Nushell screenshot — errors render as detailed miette reports rather than a bare message.*
Comments