Rust Coreutils on Ubuntu 26.04: What Breaks and How to Fix

By devopscompass
Terminal card showing ls from uutils, cp from GNU coreutils and a mktemp --suffix error on Ubuntu 26.04

Short answer: Ubuntu 26.04 LTS installs the Rust rewrite of the core utilities (uutils coreutils 0.10.0) as ls, date, sort, cat, chmod, timeout, mktemp and about a hundred more. Only cp, mv, rm and df stay GNU. In our lab most commands matched GNU output, but we found real differences: mktemp --suffix -x fails, timeout -k returns exit code 137 instead of 124, and chown -R -R is rejected. To switch back, run sudo apt-get install --allow-remove-essential coreutils-from-gnu coreutils-from-uutils-. A plain apt install coreutils-from-gnu fails with a conflict.

How this was made: on 6 November 2026 we ran every command below in a throwaway ubuntu:26.04 container (arm64, uutils 0.10.0, GNU coreutils 9.7), compared uutils against the GNU binaries that are installed next to it, and read the public bug reports. We did not test x86-64, a full server install or a reboot.

If your shell script worked on Ubuntu 24.04 and fails on 26.04 with a clap-style error: unexpected argument, or a CI job that was green until ubuntu-latest moved to 26.04 is now red, this is probably the cause. Ubuntu 25.10 introduced the Rust coreutils, and 26.04 LTS is the first long-term release to ship them, so they now reach servers and CI runners.

Which commands are Rust and which are still GNU on Ubuntu 26.04?

Run --version on a few commands. In our lab:

Terminal output showing ls date and sort from uutils 0.10.0 while cp mv rm and df are still GNU coreutils 9.7 on Ubuntu 26.04
Real output from our lab (ubuntu:26.04 container, 6 November 2026). Screenshot: DevOpsCompass.

ls, date and sort print (uutils coreutils) 0.10.0. cp, mv, rm and df print (GNU coreutils) 9.7. Four packages are involved:

Package Version in our lab What it does
coreutils-from-uutils 0.0.0~ubuntu25 Puts the Rust tools into /usr/bin (symlinks)
rust-coreutils 0.10.0-1ubuntu2~26.04.1 The uutils binary, installed in /usr/lib/cargo/bin/coreutils
gnu-coreutils 9.7-3ubuntu2.1 The GNU tools, installed with a gnu prefix (gnuls, gnudate, gnucp...)
coreutils 9.5-1ubuntu2+0.0.0~ubuntu25 An empty meta package that keeps apt happy

The gnu prefix is useful: GNU ls is always available as gnuls, and cp, mv, rm and df in /usr/bin are symlinks to gnucp, gnumv, gnurm and gnudf. Ask Ubuntu has the same split explained from the other side: rust-coreutils ships the binaries, and coreutils-from-uutils is the package that puts them on your PATH (Ask Ubuntu 1564348).

To check any machine, run ls --version | head -1 (it says uutils or GNU) and dpkg -l | grep coreutils.

Why are cp, mv and rm still GNU? Per OMG! Ubuntu, they were held back in 26.04 because of time-of-check to time-of-use (TOCTOU) security issues in the uutils versions, and Ubuntu 26.10 is meant to finish the move. So plan for the rest to change in the next release.

What did we compare, and what differed?

We ran about 200 small cases in the container, each one against both implementations: sort flags, date -d phrases, ls -l, stat -c, cut, tr, numfmt, env -S, chmod, install -D, touch, wc, du, seq and more. The great majority gave identical output. The ones that did not are the useful part:

Terminal output of mktemp with a dash suffix failing, timeout exit code 137, chown repeated flag error and different env and chmod messages on Ubuntu 26.04
Real output from our lab (ubuntu:26.04 container, 6 November 2026). Screenshot: DevOpsCompass.
Command uutils 0.10.0 GNU 9.7
mktemp --suffix -x.txt error: unexpected argument '-x' found, exit 1 creates /tmp/tmp.XXXXXXXXXX-x.txt
timeout -k 0.1 0.2 sleep 5; echo $? 137 124
chown -R -R root d the argument '--recursive' cannot be used multiple times accepted
env nosuchcmd error plus an extra line env: use -[v]S to pass options in shebang lines only the first line
chmod u+q d invalid operator (expected +, -, or =, but found q) invalid mode: 'u+q'

The last two only change error text, which matters if a script greps stderr. The first three change behaviour. The next sections take them one at a time. One more difference is cosmetic: for ls -l file dir (a file and a directory in one call) GNU pads the size column to the width of the directory's own size, and uutils does not, so the columns line up differently. A single directory listing came out identical. Do not parse ls -l by character position in either case.

A caution about this table: it is what 0.10.0 does on Ubuntu 26.04 today. The upstream project is moving fast (releases 0.11.0 on 31 August and 0.12.0 on 17 September 2026 per GitHub) and one of our findings, chown -R -R, is already closed upstream, so it may be fixed by an Ubuntu update later. Re-run the check on your own machine.

Why does mktemp --suffix with a dash fail on Ubuntu 26.04?

mktemp --suffix -x.txt passes the value as a separate word. GNU mktemp takes -x.txt as the value of --suffix. The Rust version reads it as another option and stops:

$ mktemp --suffix -x.txt
error: unexpected argument '-x' found

  tip: to pass '-x' as a value, use '-- -x'

This is the bug that broke real pipelines. On 4 November 2026 a maintainer of openstack-cron-action reported that its cleanup script, tmpfile=$(mktemp --suffix -openstack-ports.txt), started failing within milliseconds after GitHub's ubuntu-latest runner moved from 24.04 to 26.04 around 30 October (issue 127).

Fix: use the = form, which works on both implementations. We checked it:

mktemp --suffix=-x.txt        # works on uutils and on GNU

Search your repository for the pattern with grep -rn -- "--suffix \|-t -\|--prefix \+-" . and fix every hit. The same rule applies to any long option whose value starts with a dash: write --option=value and the problem cannot happen.

Does timeout behave differently?

Yes, in one case we could reproduce. With a kill-after value, GNU timeout returns 124 when the command timed out. In our lab uutils returned 137, the exit code of a process killed by SIGKILL:

$ timeout -k 0.1 0.2 sleep 5; echo "exit=$?"
exit=137

We repeated it with a command that ignores SIGTERM (bash -c 'trap "" TERM; sleep 5') and got 137 from uutils and 124 from GNU in both cases. Without -k, both returned 124. If your script tests [ $? -eq 124 ] to detect a timeout, a -k call can now take another branch.

A related report is open on the uutils tracker: "timeout: -s KILL and -k no longer kill the whole process group" (issue 15768, 25 October 2026). A project that runs commands under timeout, setsid and pkill listed two failing tests after the runner change and suspected this area, but said the cause was not confirmed (capybara-hq/plugins issue 7). We could not reproduce a surviving child process: in our test the background child of the killed command was gone with both implementations. So treat the exit code as confirmed and the process group problem as a report we did not verify.

Fix: accept both codes ([ $rc -eq 124 ] || [ $rc -eq 137 ]), or call the GNU binary explicitly with gnutimeout.

Why did my CI break when ubuntu-latest became 26.04?

GitHub moved the ubuntu-latest label to 26.04 in late October 2026 (the openstack-cron-action report puts it around 30 October, 12:00 UTC). The runner image has the Rust tools as the default, so every script that relied on a GNU quirk is a candidate. The pattern in the public reports we read is the same each time: green on ubuntu-24.04, red on ubuntu-26.04, and the failing line calls a coreutils command.

Three ways to deal with it, from fastest to best:

  1. Pin the runner to runs-on: ubuntu-24.04 while you fix the scripts. This buys time but 24.04 will not stay forever.
  2. Call the GNU binary when you cannot change the line: on 26.04 it exists as gnumktemp, gnutimeout and so on. Check command -v gnumktemp first so the script still runs on other systems.
  3. Fix the portability bug. Use --option=value, do not repeat short flags, and do not depend on exact error text. Scripts like that run on both implementations.

If you use GitHub Actions, our guide on pinning Actions to a commit SHA covers a different part of the same hygiene: build steps should not change under you without a diff you can review.

How do I switch back to GNU coreutils on Ubuntu 26.04?

You can swap the implementation with apt. We did it twice in the lab, in both directions. Do it in a test container or a VM first, because these are essential packages.

sudo apt-get install --allow-remove-essential coreutils-from-gnu coreutils-from-uutils-

The trailing - on coreutils-from-uutils- tells apt to remove that package in the same transaction. Afterwards:

Terminal output of apt-get failing with Conflicts coreutils-from then succeeding with allow-remove-essential and ls reporting GNU coreutils 9.7
Real output from our lab (ubuntu:26.04 container, 6 November 2026). Screenshot: DevOpsCompass.

ls --version printed GNU coreutils 9.7, mktemp --suffix -x.txt worked, and timeout -k 0.1 0.2 sleep 5 returned 124. To go back to the Rust tools, we ran sudo apt-get install --allow-remove-essential coreutils-from-uutils coreutils-from-gnu- and ls was uutils 0.10.0 again.

You can also switch for one command instead of the whole system: call gnumktemp, gnusort or gnudate, or put /usr/lib/cargo/bin/coreutils aside. That is the safest option for a single script.

An answer on Ask Ubuntu suggests sudo apt remove coreutils-from-uutils --allow-remove-essential and warns that packages depending on build-essential can make it harder, with a workaround using an equivs package (Ask Ubuntu 1564801). We did not test that route or build-essential; our single-transaction swap is not the same as removing the package alone. Note that Ubuntu may remove the opt-out in a later release; we only know the 26.04 package behaviour.

Why does installing coreutils-from-gnu fail?

The two packages declare Conflicts: coreutils-from, so installing one while the other is installed cannot be solved:

$ sudo apt-get install coreutils-from-gnu
 coreutils-from-gnu : Conflicts: coreutils-from
 coreutils-from-uutils : Conflicts: coreutils-from
E: Unable to satisfy dependencies. Reached two conflicting assignments:

You have to remove the other one in the same command (the - suffix above). Then apt refuses a second time, because coreutils-from-uutils is marked essential:

E: Essential packages were removed and -y was used without --allow-remove-essential.

That is why the working command needs --allow-remove-essential. Without -y, apt asks you to type a confirmation sentence instead; we ran it non-interactively. The swap printed 495 lines of output, mostly coreutils-switch diversion messages, and exited 0.

Why does env say No such file or directory in my script?

A question on Ask Ubuntu (unanswered when we checked) shows a script failing on 26.04 with an env message that looks as if env itself is broken (Ask Ubuntu 1570779). We reproduced the same message and found the usual cause. The minimal ubuntu:26.04 image has no Python, so this shebang fails:

#!/usr/bin/env python3
env: 'python3': No such file or directory
env: use -[v]S to pass options in shebang lines

The first line is the real error: python3 is not installed. The second line is a hint that the Rust env prints for every missing command and GNU env does not print. It is misleading here, because there are no options in this shebang. Install the interpreter (our Python on Ubuntu 26.04 guide covers python3, venv and pip) and the error goes away. We have not seen the exact setup of the asker, so this may not be their cause.

Does sort or ls -v still behave differently?

Two old reports say yes, so we checked on 0.10.0 (package 0.10.0-1ubuntu2~26.04.1).

  • Locale sorting. An Ask Ubuntu question from October 2025 says sort on Ubuntu 25.10 ignored locale rules and sorted by byte value (Ask Ubuntu 1557259). In our lab, with en_US.UTF-8 generated, uutils and GNU gave the same order for b B a A à C _x (a A à b B C _x). The same held for sort -f and sort -h.
  • ls -v. On 25 September 2026 the PostgreSQL packaging team reported that ls -1v no longer sorted pg_config paths by version on Ubuntu resolute, which broke their tests (uutils issue 14859). The Ubuntu package changelog says that version was backported as 0.10.0-1ubuntu2~26.04.1. In our test ls -1v returned f1 f2 f9 f10, the same as GNU.

So on a current 26.04 these two are fixed. If you see them, your package is probably older: run sudo apt update && sudo apt upgrade.

What else do people report?

These are reports we read and did not reproduce. Treat them as things to test in your own setup, not as facts:

  • install -D was not safe when run in parallel by make -j2, with intermittent "cannot create directory" errors while building packages for Ubuntu resolute (uutils issue 14862, closed).
  • A user on Ask Ubuntu said the new du printed wrong sizes on Ubuntu 25.10 (Ask Ubuntu 1559396). Our du -sh and du -sb cases matched GNU on 26.04.
  • The upstream tracker has an open issue titled "~180 differences between GNU coreutils and uutils, found by AI fuzzer" (issue 15254, 11 October 2026). We did not go through it. It is a sign that edge cases exist, not a list of things that break normal scripts.

What we did not test

  • x86-64 (our host is arm64), a full server install, a desktop, a reboot, or the GitHub runner image itself. The CI part is based on public reports.
  • Performance. We make no claim that Rust coreutils are faster or slower.
  • About 130 more cases that we had written never ran: we stopped the second comparison batch after 151 of 285 cases because the sleep and timeout cases are slow. A number of differences in its log were mistakes of our script (command order, exit codes of pipes), not of uutils. We only report differences that we re-ran by hand.
  • Removing coreutils-from-uutils on its own, the build-essential workaround, and unattended upgrades after the swap.
  • The security audit itself. We only know its headline from Hacker News and press titles.

FAQ

Does Ubuntu 26.04 use Rust coreutils?

Yes. Ubuntu 26.04 LTS installs uutils coreutils 0.10.0 for most commands, including ls, date, sort, cat, chmod and mktemp. In our lab cp, mv, rm and df were still GNU coreutils 9.7. Run ls --version | head -1 to check your own system.

How do I check which coreutils my Ubuntu uses?

Run ls --version | head -1: it prints (uutils coreutils) or (GNU coreutils). For the packages, run dpkg -l | grep coreutils. The GNU tools are also installed with a gnu prefix, for example gnuls, so you can compare both on one machine.

How do I switch back to GNU coreutils on Ubuntu 26.04?

Run sudo apt-get install --allow-remove-essential coreutils-from-gnu coreutils-from-uutils-. A plain apt install coreutils-from-gnu fails because both packages conflict. We tested the swap in a container in both directions. Try it in a VM before a production server.

Is the Rust coreutils safe to use after the security audit?

Canonical commissioned a security audit, and Hacker News carried the headline "Ubuntu Rust Coreutils Audit Revealed 113 Issues" in April 2026 (HN discussion). We did not read the audit, so we cannot judge it. The audit is also the reason cp, mv and rm stayed GNU in 26.04, per OMG! Ubuntu.

Will Ubuntu 26.10 move cp, mv and rm to Rust too?

According to OMG! Ubuntu and Phoronix, yes: Ubuntu 26.10 is meant to complete the move and ship all utilities in Rust, after the TOCTOU issues in the uutils versions were fixed upstream. This is press reporting from September 2026. We did not test 26.10.

Why does timeout return 137 instead of 124 on Ubuntu 26.04?

In our test, timeout -k 0.1 0.2 sleep 5 returned 137 with uutils and 124 with GNU. Without -k both returned 124. A related upstream issue about -k and -s KILL is open (issue 15768). Accept both exit codes in scripts, or call gnutimeout.

Sources

Last verified: November 2026 (6 November 2026, uutils coreutils 0.10.0 and GNU coreutils 9.7 on ubuntu:26.04, arm64, container only). Package versions move with Ubuntu updates; run ls --version on your own system.

Header image: Image: DevOpsCompass.

Comments