Just Cheat Sheet
Overview
Just is a command runner that lets you save and run project-specific commands in a file called justfile. Unlike Make, Just is not a build system — it focuses purely on running commands. It supports variables, arguments, conditional execution, and works across Linux, macOS, and Windows.
Just uses a syntax inspired by Make but without the quirks like tab-sensitivity for indentation or implicit rules. Recipes in a justfile are simply named sets of shell commands that can accept parameters, have dependencies, and use built-in functions for common tasks.
Installation
# macOS
brew install just
# Linux - prebuilt binary
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
# Cargo (any platform)
cargo install just
# Arch Linux
sudo pacman -S just
# Ubuntu/Debian (via prebuilt .deb)
wget https://github.com/casey/just/releases/latest/download/just-x86_64-unknown-linux-musl.tar.gz
tar xzf just-x86_64-unknown-linux-musl.tar.gz just
sudo mv just /usr/local/bin/
# Verify
just --version
Shell Completion
# Bash
just --completions bash > ~/.local/share/bash-completion/completions/just
# Zsh
just --completions zsh > ~/.zfunc/_just
# Fish
just --completions fish > ~/.config/fish/completions/just.fish
Core Commands
| Command | Description |
|---|---|
just | Run the default recipe |
just <recipe> | Run a specific recipe |
just <recipe> arg1 arg2 | Run recipe with arguments |
just --list | List available recipes |
just --summary | One-line summary of recipes |
just --show <recipe> | Show recipe source |
just --evaluate | Show variable values |
just --dry-run <recipe> | Print commands without running |
just --choose | Select recipe interactively (needs fzf) |
just --fmt | Format the justfile |
just --check --fmt | Check formatting without changes |
Justfile Syntax
Basic Recipes
# justfile
# Default recipe (first one listed)
default:
echo "Hello from just!"
# Simple build recipe
build:
cargo build --release
# Recipe with multiple commands
deploy: build
scp target/release/myapp server:/opt/app/
ssh server "systemctl restart myapp"
# Recipe with arguments
greet name:
echo "Hello, {{name}}!"
# Default argument values
serve port='8080':
python3 -m http.server {{port}}
Variables and Settings
# Variables
version := "1.0.0"
build_dir := "target/release"
image := "myapp:" + version
# Environment variables
export DATABASE_URL := "postgres://localhost/mydb"
# Settings
set dotenv-load # Load .env file
set shell := ["bash", "-cu"]
set positional-arguments
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
Dependencies and Ordering
# Recipe dependencies
build: clean compile test
echo "Build complete"
clean:
rm -rf build/
compile:
gcc -o build/app src/main.c
test: compile
./build/app --test
# Run dependency with arguments
push version: (build version)
git tag {{version}}
git push --tags
build version:
echo "Building version {{version}}"
Configuration
Conditional Execution
# OS-specific commands
install:
#!/usr/bin/env bash
if [[ "{{os()}}" == "macos" ]]; then
brew install myapp
elif [[ "{{os()}}" == "linux" ]]; then
sudo apt install myapp
fi
# Conditional variable
db := if env_var_or_default("CI", "") == "true" { "postgres" } else { "sqlite" }
# Error handling
check:
test -f config.yaml || (echo "Missing config!" && exit 1)
Built-in Functions
# System info
info:
echo "OS: {{os()}}"
echo "Arch: {{arch()}}"
echo "Home: {{env_var('HOME')}}"
echo "Invocation dir: {{invocation_directory()}}"
echo "Just executable: {{just_executable()}}"
# String functions
process name:
echo "Upper: {{uppercase(name)}}"
echo "Lower: {{lowercase(name)}}"
echo "Replaced: {{replace(name, '-', '_')}}"
# Path functions
paths:
echo "Parent: {{parent_directory(justfile())}}"
echo "Stem: {{file_stem('archive.tar.gz')}}"
echo "Extension: {{extension('photo.jpg')}}"
Shebang Recipes
# Python recipe
analyze:
#!/usr/bin/env python3
import json
with open("data.json") as f:
data = json.load(f)
print(f"Records: {len(data)}")
# Node recipe
transform:
#!/usr/bin/env node
const fs = require('fs');
const data = fs.readFileSync('input.txt', 'utf8');
console.log(data.toUpperCase());
Advanced Usage
Private Recipes and Documentation
# Documented recipe (shown in --list)
# Build the application for production
build:
cargo build --release
# Private recipe (hidden from --list)
[private]
_helper:
echo "internal helper"
# Grouped recipes with doc comments
[group('database')]
db-migrate:
diesel migration run
[group('database')]
db-reset:
diesel database reset
# Confirm before running
[confirm("Are you sure you want to deploy?")]
deploy:
./deploy.sh production
Working Directory and Imports
# Change working directory for recipe
[working-directory('frontend')]
build-frontend:
npm run build
# Import other justfiles
import 'tasks/docker.just'
import? 'local.just' # Optional import
# Modules
mod docker 'tasks/docker.just'
# Usage: just docker build
Variadic Arguments
# Variadic positional arguments
test *args:
cargo test {{args}}
# One-or-more variadic
docker +services:
docker compose up {{services}}
# Combining fixed and variadic
run command *args:
{{command}} {{args}}
Troubleshooting
| Issue | Solution |
|---|---|
error: Expected '*', '## ', '$', '(', '+', '/', '@'... | Check indentation — use spaces or tabs consistently |
| Recipe not found | Run just --list to see available recipes |
.env not loaded | Add set dotenv-load to justfile |
| Wrong shell on Windows | Set set windows-shell appropriately |
| Variables not expanding | Use {{var}} syntax, not $(var) or $var |
| Can’t find justfile | Just searches parent directories; check file name is exactly justfile |
# Debug: show what would run
just --dry-run build
# Show all variables
just --evaluate
# Dump complete justfile after imports
just --dump
# Use a different justfile
just --justfile path/to/justfile build
# Override a variable
just version="2.0" build