Use just as a Modern Task Runner
Learn to use just as a project task runner: write justfiles, define variables, chain recipe dependencies, and manage tasks across multiple projects.
Before you start
- ▸A terminal and a user account with sudo access for package installation
- ▸Basic familiarity with the command line and shell scripting
- ▸An existing project directory to add a justfile to
just is a command runner that stores project tasks in a justfile. Think of it as a Makefile without the build-system baggage — no implicit rules, no tab-character traps, no automatic dependency tracking on files. You write named recipes, run them with just recipename, and that's the contract. It's cross-platform, actively maintained, and has become the de-facto standard task runner in many Rust, Python, and Go projects.
Installing just
Debian / Ubuntu
just is packaged in Ubuntu 24.04 and Debian 13 (Trixie) onwards. On older releases, install from the upstream binary or via cargo.
sudo apt install just
Fedora / RHEL family
sudo dnf install just
Arch Linux
sudo pacman -S just
Any distro — via cargo
cargo install just
Any distro — prebuilt binary
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin
Confirm the installation:
just --version
Your First justfile
Create a file named justfile (or .justfile) in your project root. just searches upward from the current directory, so you only need one file per project tree.
cat > justfile <<'EOF'
# Build the project
build:
cargo build --release
# Run tests
test:
cargo test
# Remove build artifacts
clean:
cargo clean
EOF
Run a recipe:
just build
List all available recipes:
just --list
Output will look similar to:
Available recipes:
build # Build the project
clean # Remove build artifacts
test # Run tests
The leading four spaces before each command are plain spaces — not tabs. This is one of just's biggest quality-of-life improvements over make.
Variables and Interpolation
Variables are declared at the top level with := and interpolated with {{variable}}.
cat > justfile <<'EOF'
version := "1.4.2"
registry := "ghcr.io/myorg"
image := registry + "/myapp:" + version
build-image:
docker build -t {{image}} .
push-image: build-image
docker push {{image}}
EOF
You can override any variable on the command line without editing the file:
just version=2.0.0 build-image
Environment variables and .env files
just can load a .env file automatically. Add this line at the top of your justfile:
set dotenv-load
Now any variable in .env is available to recipes as a shell environment variable, not as a just variable — the distinction matters for interpolation vs. shell expansion.
Recipe Parameters
Recipes accept positional parameters with optional defaults.
cat > justfile <<'EOF'
# Deploy to a named environment, default: staging
deploy env="staging":
echo "Deploying to {{env}}"
./scripts/deploy.sh {{env}}
# Migrate database; pass a specific revision or 'head'
db-migrate rev="head":
alembic upgrade {{rev}}
EOF
just deploy production
just db-migrate 3a9c12f
Recipe Dependencies
List dependencies before the colon body to run them first. Dependencies run in order, left to right, and each runs only once per just invocation even if referenced multiple times.
cat > justfile <<'EOF'
lint:
ruff check .
format:
ruff format .
test: lint
pytest -q
ci: format lint test
echo "All checks passed"
EOF
just ci
Running just ci executes format, then lint (once, despite being a dependency of both ci and test), then test, then the echo.
Silencing and continuing on error
Prefix a command with @ to suppress echoing it, or with - to continue even if it fails.
clean:
-rm -rf dist/
@echo "Clean done"
Conditional Logic and Shell Features
Each line in a recipe is a separate shell invocation by default. To write multi-line shell logic, use a shebang to drop into a single shell session.
check-env:
#!/usr/bin/env bash
set -euo pipefail
if [[ -z "${DATABASE_URL:-}" ]]; then
echo "ERROR: DATABASE_URL is not set" >&2
exit 1
fi
echo "Environment looks good"
just also provides a built-in conditional expression for variable assignment:
os := if os() == "macos" { "darwin" } else { "linux" }
download-tool:
curl -L https://example.com/tool-{{os}} -o ~/.local/bin/tool
chmod +x ~/.local/bin/tool
Working Across Multiple Projects
just walks up the directory tree looking for a justfile, so you can run project recipes from any subdirectory. For managing multiple repos or a monorepo, two patterns work well.
Pattern 1 — Root justfile delegates to subdirectories
cat > justfile <<'EOF'
# Run any recipe in the frontend workspace
frontend *args:
cd frontend && just {{args}}
# Run any recipe in the backend workspace
backend *args:
cd backend && just {{args}}
# Build everything
all: (frontend "build") (backend "build")
EOF
just frontend test
just backend deploy staging
Pattern 2 — Global justfile for personal shortcuts
Store a global justfile in ~/.justfile and invoke it from anywhere with:
just --global-justfile recipename
Add an alias to your shell config so you don't have to type the flag every time:
echo "alias jj='just --global-justfile'" >> ~/.bashrc
source ~/.bashrc
Verifying Everything Works
# Check just can parse your justfile without running anything
just --dry-run ci
--dry-run prints every command that would execute without running a single one. Use it to audit complex dependency chains before committing them to CI.
# Dump all variables and their values
just --evaluate
Troubleshooting
- "error: Expected end of file, but found ..." — You have a syntax error. Common cause: using
=instead of:=for variable assignment. - Recipe silently does nothing — Check that your recipe body lines are indented with spaces, not tabs. While
justdoes accept tabs, mixing them causes confusing parse failures on some versions. - Environment variable not visible in recipe — Each recipe line runs in a fresh shell. Export the variable explicitly, use
set dotenv-load, or switch the recipe to a shebang block (#!/usr/bin/env bash) so all lines share one shell process. justpicks up the wrong justfile — Usejust --justfile /path/to/justfile recipenameto be explicit, or check which filejust --listresolves to from your current directory.- Old package version missing features — Shebang recipes,
set dotenv-load, and variadic*argsrequirejust1.0+. Check withjust --versionand update viacargo install justif your distro ships an older build.
Frequently asked questions
- How is just different from make?
- make is a build system that tracks file timestamps and has implicit rules. just is purely a task runner with no file tracking, no implicit rules, and no tab-only indentation requirement — it's simpler and less error-prone for running commands.
- Can I use just in CI/CD pipelines?
- Yes. Install just in your CI environment the same way you would locally, then call just recipename in your pipeline steps. This keeps CI scripts and local dev workflows in sync.
- Does just support Windows?
- Yes, just has first-class Windows support with prebuilt binaries. Recipes use cmd.exe or PowerShell by default on Windows unless you specify a shebang line pointing to bash or another interpreter.
- How do I pass multiple arguments to a recipe?
- Use variadic parameters: declare the last parameter with a * prefix (e.g., test *flags) and all extra arguments are concatenated into that variable as a single string.
- Can just load secrets from a .env file automatically?
- Yes. Add set dotenv-load at the top of your justfile and just will read a .env file in the same directory, exposing its contents as shell environment variables inside recipes.
Related guides
Bash Arrays and Associative Arrays
Master bash indexed and associative arrays: declaration, element access, looping, mapfile, namerefs, and practical patterns for real scripting work.
Bash Functions and Variable Scoping
Master Bash function scoping with local variables, source-based libraries, correct use of return codes, and array passing techniques including namerefs.
Bash Loops: for, while and until
Learn all three Bash loop types — for, while, and until — with practical, copy-paste examples covering file iteration, counting, polling, and safe line reading.
Bash Scripting for Beginners
Learn Bash scripting from scratch: shebang lines, variables, conditionals, loops, and arguments, plus a real backup script to tie it all together.