Most people meet Allure through one of these two lines:
allure: command not found # Linux
'allure' is not recognized as the name of a cmdlet ... # Windows
Same cause, two dialects. This post fixes both, then goes past the install — because a working
allure command still gives you a report nobody wants to read until you feed it a few extra
things.
Versions below were checked on 2026-08-29. Check the releases page for today’s number.
Contents
- The part everyone skips: what Allure actually is
- Install on Linux
- Install on Windows
- Check it worked
- Your first report from pytest
- serve vs generate vs open
- Make the report worth reading
- Trends and history
- Troubleshooting table
- Cheat sheet
1. The part everyone skips: what Allure actually is
Allure is not a test runner and not a pytest plugin. It is a Java program that reads a folder of JSON files and renders a static HTML site.
There are two separate pieces, installed in two different places:
| Piece | Job | Installed with | Lives in | Current |
|---|---|---|---|---|
| Adapter | Writes allure-results/*.json while tests run |
pip install allure-pytest |
your virtualenv, per project | 2.16.0 |
| Command line | Turns allure-results/ into HTML |
apt / dpkg / scoop | your machine, once | 2.46.0 |
Three consequences worth internalising:
- Their versions are unrelated. Adapter 2.16.0 with command line 2.46.0 is normal, not a bug.
pip install allure-pytestdoes not give you theallurecommand. This is the single most common misunderstanding. The pip package writes results; it does not render them.- You need a JRE. Allure 2 compiles to Java 8 bytecode, so Java 8 or newer — anything current is fine.
There are adapters for most ecosystems (allure-pytest, allure-behave, JUnit, TestNG, Jest,
Cypress, NUnit, RSpec). The command line is the same binary for all of them, and everything below
section 6 applies whichever adapter you use.
2. Install on Linux
Debian and Ubuntu, three commands:
sudo apt-get install -y default-jre
wget https://github.com/allure-framework/allure2/releases/download/2.46.0/allure_2.46.0-1_all.deb
sudo dpkg -i allure_2.46.0-1_all.deb
_all.debmeans architecture-independent. The same file works on x86_64 and ARM — it is Java.- Red Hat, Fedora, SUSE: the same release ships
allure_2.46.0-1.noarch.rpm. - If
dpkg -istops on unmet dependencies, runsudo apt-get -f installand it will finish.
No root, or a distro with neither deb nor rpm? Use the tarball:
wget https://github.com/allure-framework/allure2/releases/download/2.46.0/allure-2.46.0.tgz
tar -zxvf allure-2.46.0.tgz -C ~/.local/share/
echo 'export PATH="$HOME/.local/share/allure-2.46.0/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
If your shell is zsh — the default on macOS and on a growing number of distros — write that
export line into ~/.zshrc instead. Appending it to ~/.bashrc on a zsh machine leaves PATH
unchanged, with nothing on screen to say why.
On WSL: treat it as Linux and use the commands above. Do not try to call a
Windows-installed allure.bat from inside WSL — the path translation will bite you. Install it
twice if you work on both sides.
3. Install on Windows
Use Scoop. Manual unzip-and-edit-PATH is precisely what produces the
is not recognized error.
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
scoop install allure
Why this works where the manual route does not: the Scoop manifest extracts the release zip, sets
ALLURE_HOME, and creates a shim for bin\allure.bat. Nothing left for you to wire up. Upgrades
are scoop update allure.
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser is needed because the Scoop installer is a
downloaded script. It applies to your user account only, not the machine.
One thing catches nearly everyone: a new PATH only exists in a new shell. If
allure --version fails immediately after install, close the terminal, open a fresh one, and try
again before debugging anything else. This also applies to the VS Code integrated terminal, which
inherits its environment from whenever VS Code was started — restart VS Code, not just the panel.
If that combination of PATH and execution policy sounds familiar, it is the same pair of problems
that makes a fresh Python install on Windows so unpleasant. I wrote those up separately in
setting up Python on Windows 11.
If corporate policy blocks Scoop, there is no Chocolatey package for Allure either. Fall back to the zip:
- Download
allure-2.46.0.zipfrom the releases page. - Extract to a path with no spaces, e.g.
C:\tools\allure-2.46.0. - Add
C:\tools\allure-2.46.0\binto your userPATH(System Properties → Environment Variables → userPath→ New). - Open a new terminal.
4. Check it worked
java -version # must print something; Allure is a Java program
allure --version # should print 2.46.0
Run both. If allure --version fails but java -version works, it is a PATH problem. If
java -version fails, install the JRE first — the error Allure gives you for a missing Java is not
a helpful one.
5. Your first report from pytest
--alluredir is not a pytest flag. It appears only once the adapter is installed:
pip install allure-pytest
pytest tests/ --alluredir=allure-results --clean-alluredir
allure serve allure-results
--clean-alluredir wipes the results directory first. Get in the habit now: Allure merges whatever
it finds in that folder, so without it a test you deleted last week still shows up in today’s
report, passing.
Your browser opens on a report. Nothing was uploaded anywhere — allure serve starts a local web
server and stops when you press Ctrl+C.
6. serve vs generate vs open
Three commands, three different jobs:
# Throwaway. Generate to a temp dir, start a local server, open the browser.
allure serve allure-results
allure serve allure-results --port 8080 # if the default port clashes
# Persistent. Write a real directory you can archive or publish.
allure generate allure-results --clean --output allure-report
# View a directory produced by 'generate'.
allure open allure-report
# One self-contained HTML file — for a ticket attachment, an email, a CI artifact.
allure generate allure-results --clean --single-file --output allure-report
Windows is identical; quote paths that contain spaces:
allure serve "C:\projects\my-tests\allure-results"
allure generate "C:\projects\my-tests\allure-results" --clean --single-file --output allure-report
Why allure open and not just double-clicking index.html: the report fetches its data over
XHR, and browsers block that on the file:// protocol. Double-clicking gives you a rendered page
with no test results in it, which looks like a generation failure and is not one. Use allure open
for local viewing, or --single-file when the report has to travel as one attachment.
One caveat on --single-file: the report data is inlined into that one HTML file rather than
sitting in folders beside it, and attachments are part of that data. The file therefore grows with
everything your suite attaches, so a run that screenshots every failure produces a much larger file
than the pass/fail data alone would suggest. Check the size against your mail provider’s attachment
limit before you rely on emailing it. It is the right format for a ticket or a CI artifact; for a
large suite, publish the directory and send a link.
7. Make the report worth reading
Out of the box you get pass/fail and a stack trace. Four additions turn it into something a non-author can actually diagnose from.
Steps and attachments
import allure
@allure.step("Log in as {username}")
def login(page, username, password):
...
def test_checkout(page):
login(page, "demo", "secret")
allure.attach(
page.screenshot(),
name="cart page",
attachment_type=allure.attachment_type.PNG,
)
allure.attach.file("app.log", name="server log",
attachment_type=allure.attachment_type.TEXT)
@allure.step gives a failing test a readable timeline instead of one opaque block. The step title
is formatted with the call’s arguments, so {username} is filled in per call — and it works whether
the argument was passed positionally or by keyword. (page.screenshot() above is Playwright; swap
in whatever your driver returns bytes from.)
allure.attach takes bytes or a string; allure.attach.file takes a path. The supported
attachment_type values are PNG, JPG, SVG, GIF, BMP, TIFF, TEXT, HTML, JSON,
XML, YAML, CSV, TSV, URI_LIST, PDF, ZIP, PCAP, MP4, OGG and WEBM.
The type you pass decides whether the report can show the evidence or only offer it for download:
| You attach | The report gives you |
|---|---|
PNG, JPG, GIF, BMP, TIFF, SVG |
The image, inline |
MP4, OGG, WEBM |
A real video player |
CSV, TSV |
A rendered table, not raw text |
JSON, XML, YAML |
Syntax-highlighted code |
HTML, TEXT, URI_LIST |
Rendered inline |
PDF, ZIP, PCAP |
A download link only |
Attach a CSV as TEXT and you get a wall of commas; attach it as CSV and you get a table. Same
file, one-word change.
Two size limits apply to previews. Above 10 MiB the preview is dropped
and the attachment degrades to a plain download link; above 2 MiB syntax highlighting is
dropped. Photos and video are exempt from the 10 MiB rule, so a large screenshot still displays —
but SVG is not exempt, because it is text.
The high-value habit: attach the evidence on failure, automatically, from a fixture or
pytest_runtest_makereport hook, so nobody has to reproduce a failure to see what the screen
looked like.
environment.properties — what was this run against?
Drop a plain properties file into the results directory and it becomes an Environment widget on the report’s front page:
# allure-results/environment.properties
Browser=Chrome 141
OS=Ubuntu 24.04
Build=2026.08.28-1044
Target.URL=https://staging.example.com
Generate it from your test session rather than committing it — the point is recording what this run used. Without it, a report three weeks old tells you nothing about what it was testing.
categories.json — stop triaging the same failure twice
By default every failure is “failed”. categories.json sorts them into buckets by regex, so
infrastructure noise stops looking like product defects:
[
{
"name": "Infrastructure problem",
"matchedStatuses": ["broken"],
"messageRegex": ".*ConnectionError.*|.*Timeout.*"
},
{
"name": "Outdated test",
"matchedStatuses": ["broken"],
"traceRegex": ".*NoSuchElementException.*"
},
{
"name": "Product defect",
"matchedStatuses": ["failed"]
}
]
Two rules decide whether this file does anything, and neither is obvious:
The patterns have to match the whole message, not a fragment. That is why every regex above is
wrapped in .*. Write "messageRegex": "ConnectionError" and you get no matches and no error — the
category simply renders empty, which looks exactly like Allure ignoring the file. (Allure compiles
these with DOTALL, so . spans newlines and .* does reach into a multi-line stack trace.)
Order matters, because a test lands in the first category it matches and no others. That is why
the catch-all Product defect sits last. Move it to the top and it swallows every failure before
the specific rules get a look, leaving you exactly where you started. Put your narrow rules first
and the broad ones last. Anything matching nothing falls into Allure’s built-in “Product defects”
or “Test defects” bucket.
Copy it into allure-results/ before generating. On a suite with a hundred failures this is the
difference between an afternoon of triage and five minutes.
executor.json — link the report back to the build
{
"name": "Jenkins",
"type": "jenkins",
"buildOrder": 412,
"buildName": "nightly-regression #412",
"buildUrl": "https://ci.example.com/job/nightly/412/",
"reportUrl": "https://ci.example.com/job/nightly/412/allure/"
}
Also goes in allure-results/. It puts a clickable build reference on the report, so a report
someone forwarded you six weeks later is still traceable to a pipeline run.
buildOrder is optional but worth setting: it is what labels each point on the trend graph. Allure
renders it as #412, so when you spot a dip in the pass rate you can tell which build it was.
Without it the points are still plotted in the right order — ordering comes from the chain of
reports, not from this field — but they carry no label.
type only selects the icon shown next to the build link. Allure ships icons for jenkins,
github, gitlab, bitbucket, teamcity, bamboo, circleci and azure; anything else falls
back to a generic one.
8. Trends and history
Trend graphs are the first thing people ask for and the first thing that silently does not work.
Allure has no database. History lives in a history/ folder inside the generated report, and it is
only carried forward if you carry it forward — copy it from the previous report into the new
results directory before generating:
# after the previous run's report exists
cp -r allure-report/history allure-results/ 2>/dev/null || true
allure generate allure-results --clean --output allure-report
On CI this means archiving the previous allure-report/ as a build artifact and restoring it at
the start of the next run. Skip that and every report shows a single data point and looks like the
first run you have ever done.
Copying the folder is necessary but not sufficient. History is keyed on each test’s historyId,
which is derived from its full name and its parameters. Rename a test, move it to another module, or
change what you parametrise it with, and that test starts a fresh history line with one point in it
while everything around it keeps its trend. That is expected behaviour, not a failed copy.
Pin the command line version in CI. A build image that resolves allure to whatever is newest
generates each run’s report with a potentially different binary — an extra moving part in the one
artefact you are using to diagnose everything else. Use the exact .deb URL:
RUN apt-get update && apt-get install -y default-jre wget \
&& wget -q https://github.com/allure-framework/allure2/releases/download/2.46.0/allure_2.46.0-1_all.deb \
&& dpkg -i allure_2.46.0-1_all.deb \
&& rm allure_2.46.0-1_all.deb
Package managers do not all track the release at the same pace. Checked on 2026-08-29: GitHub
2.46.0, Scoop 2.46.0, Homebrew 2.46.0, npm allure-commandline 2.43.0 — so Scoop and Homebrew are
currently level with the release, and npm is three minor versions behind. For a workstation, take
whatever your package manager gives you; for CI, pin.
9. Troubleshooting table
| Symptom | Cause | Fix |
|---|---|---|
allure: command not found / is not recognized |
Not on PATH, or PATH changed in an already-open shell |
Open a new terminal; then scoop install allure / dpkg -i |
allure --version fails, java -version also fails |
No JRE | sudo apt-get install default-jre |
unrecognized arguments: --alluredir |
allure-pytest not installed in the active virtualenv |
pip install allure-pytest |
| Report opens but is empty | Opened index.html over file:// |
allure open allure-report, or generate --single-file |
| Deleted tests still in the report | Stale results merged | Add --clean-alluredir to the pytest run |
| Report shows “Unknown” / no trend | history/ not carried forward |
Copy allure-report/history into allure-results/ before generating |
| Trend points have no build labels | No buildOrder in executor.json |
Add your CI build number as buildOrder |
| Trend never shows more than 20 points | Allure caps trend history at 20 | Expected, not a bug |
| One test lost its history, the rest kept theirs | Test renamed or reparametrised, so its historyId changed |
Expected — it rebuilds from this run onwards |
| A category renders empty | Regex matched a fragment, not the whole message | Wrap the pattern in .* |
| Every failure is one undifferentiated pile | No categories.json |
Add one to allure-results/ |
dpkg -i fails on dependencies |
Missing transitive packages | sudo apt-get -f install |
| Scoop install blocked by policy | Execution policy or IT restriction | Zip + manual PATH; there is no Chocolatey package |
10. Cheat sheet
# Linux install
sudo apt-get install -y default-jre
wget https://github.com/allure-framework/allure2/releases/download/2.46.0/allure_2.46.0-1_all.deb
sudo dpkg -i allure_2.46.0-1_all.deb
# Windows install
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
scoop install allure
# Everyday use
pip install allure-pytest
pytest tests/ --alluredir=allure-results --clean-alluredir
allure serve allure-results
# Persistent report with history
cp -r allure-report/history allure-results/ 2>/dev/null || true
allure generate allure-results --clean --output allure-report
allure open allure-report
Files Allure picks up from allure-results/: environment.properties, categories.json,
executor.json, history/.
References
- Allure 2 releases — https://github.com/allure-framework/allure2/releases
- Scoop — https://scoop.sh
allure-pyteston PyPI — https://pypi.org/project/allure-pytest/- Linux
command not foundthread — https://stackoverflow.com/questions/43875741/allure-command-not-found-on-linux - Windows
is not recognizedthread — https://stackoverflow.com/questions/70885555/allure-report-generation-fails-with-message-allure-is-not-recognized-as-the-n
If one of those two error messages is what brought you here, which one was it? They need different
fixes, and I would like to know which half of this post is doing the work. And if you got the
install sorted but your trend graph still shows a single lonely point, leave a comment describing
what your CI does between runs — that one is nearly always the history/ folder not surviving the
build, and it is far easier to spot with the pipeline in front of us.
Questions or comments?
Ask a question, share your experience, or add a correction below.