Just Command Runner Installation and Usage
Just is a handy command runner written in Rust that uses a justfile to define project-specific commands called recipes. Unlike Makefiles, Just focuses purely on running commands rather than build dependency tracking, making it simpler and more predictable for project automation on Linux.
Prerequisites
- Linux (Ubuntu/Debian or CentOS/Rocky)
- Basic shell knowledge
- A project directory to automate
Installing Just
# Install via cargo (Rust package manager)
cargo install just
# Ubuntu/Debian - prebuilt binary via apt
curl -fsSL https://just.systems/install.sh | bash -s -- --to /usr/local/bin
# Or download directly from GitHub releases
curl -sL https://github.com/casey/just/releases/latest/download/just-x86_64-unknown-linux-musl.tar.gz | \
tar -xz -C /usr/local/bin just
# macOS via Homebrew (for teams with mixed environments)
brew install just
# Verify installation
just --version
Enable shell completion:
# Bash completion
just --completions bash > /etc/bash_completion.d/just
# Zsh completion
just --completions zsh > /usr/local/share/zsh/site-functions/_just
# Fish completion
just --completions fish > ~/.config/fish/completions/just.fish
Basic Justfile Syntax
Create a justfile in your project root:
# justfile
# Default recipe - shown when running `just` without arguments
default:
@just --list
# Simple recipe
build:
go build -o bin/app ./cmd/app
# Recipe with description (shown in list)
# Run all tests with coverage
test:
go test ./... -v -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html
# Recipe that calls another recipe
ci: lint test build
echo "CI pipeline complete"
# Quiet recipe (suppress command echoing)
clean:
@rm -rf bin/
@rm -f coverage.out coverage.html
@echo "Cleaned build artifacts"
Run recipes:
# List all recipes
just --list
just -l
# Run a recipe
just build
# Run default recipe
just
# Run multiple recipes in sequence
just clean build test
# Show what commands would run (dry run)
just --dry-run build
Recipes with Arguments and Variables
# Variables (set at top of justfile)
app_name := "myapp"
version := `git describe --tags --always`
build_date := `date -u +"%Y-%m-%dT%H:%M:%SZ"`
# Recipe with required argument
tag release_version:
git tag -a {{release_version}} -m "Release {{release_version}}"
git push origin {{release_version}}
# Recipe with default argument
serve port="8080":
@echo "Starting server on port {{port}}"
./bin/{{app_name}} --port {{port}}
# Recipe with multiple arguments
deploy env="staging" region="us-east-1":
@echo "Deploying {{app_name}} to {{env}} in {{region}}"
./scripts/deploy.sh {{env}} {{region}}
# Build with version info
build-release:
go build \
-ldflags "-X main.Version={{version}} -X main.BuildDate={{build_date}}" \
-o bin/{{app_name}} \
./cmd/app
Call recipes with arguments:
# Pass argument to recipe
just tag v1.2.3
# Override default argument
just serve 9090
# Multiple arguments
just deploy production eu-west-1
Environment Variables
# Set environment variables for all recipes
export LOG_LEVEL := "info"
export APP_ENV := "development"
# Load from .env file
set dotenv-load
# Recipe that uses env vars
start:
@echo "Starting in $APP_ENV mode"
./bin/app
# Override env for specific recipe
start-debug:
LOG_LEVEL=debug ./bin/app
Conditionals and Control Flow
os := os()
arch := arch()
# Conditional based on OS
install-deps:
#!/usr/bin/env bash
set -euo pipefail
if [ "{{os}}" = "linux" ]; then
sudo apt-get install -y curl wget git
elif [ "{{os}}" = "macos" ]; then
brew install curl wget git
else
echo "Unsupported OS: {{os}}"
exit 1
fi
# Assert conditions before running
deploy-prod:
#!/usr/bin/env bash
set -euo pipefail
if [ -n "$(git status --porcelain)" ]; then
echo "Error: uncommitted changes detected"
exit 1
fi
if [ "$(git branch --show-current)" != "main" ]; then
echo "Error: must be on main branch to deploy"
exit 1
fi
./scripts/deploy.sh production
# Conditional recipe selection
build target="native":
#!/usr/bin/env bash
case "{{target}}" in
native) go build -o bin/app ./cmd/app ;;
linux) GOOS=linux GOARCH=amd64 go build -o bin/app-linux ./cmd/app ;;
darwin) GOOS=darwin GOARCH=arm64 go build -o bin/app-darwin ./cmd/app ;;
*) echo "Unknown target: {{target}}"; exit 1 ;;
esac
Cross-Platform Support
Just provides built-in functions for cross-platform compatibility:
# Built-in OS and architecture detection
os_info:
@echo "OS: {{os()}}"
@echo "Arch: {{arch()}}"
@echo "Home: {{home_directory()}}"
# Use PowerShell on Windows, bash on Unix
test:
#!/usr/bin/env bash
go test ./...
# Path separator handling
config_path := if os() == "windows" { "C:\\config" } else { "/etc/myapp" }
# Cross-platform binary name
binary := if os() == "windows" { "app.exe" } else { "app" }
build:
go build -o bin/{{binary}} ./cmd/app
# Shell override per recipe
legacy-script:
#!/bin/sh
echo "Using POSIX sh"
ls -la
Project Automation Patterns
Docker Workflow
image := "myapp"
registry := "registry.example.com"
tag := `git rev-parse --short HEAD`
# Build Docker image
docker-build:
docker build -t {{image}}:{{tag}} -t {{image}}:latest .
# Push to registry
docker-push: docker-build
docker tag {{image}}:{{tag}} {{registry}}/{{image}}:{{tag}}
docker push {{registry}}/{{image}}:{{tag}}
docker push {{registry}}/{{image}}:latest
# Run local development stack
up:
docker compose up -d
@echo "Services started. Run 'just logs' to follow logs"
down:
docker compose down
logs service="":
docker compose logs -f {{service}}
Database Migrations
db_url := env_var_or_default("DATABASE_URL", "postgres://localhost/myapp")
# Run migrations
migrate-up:
migrate -database "{{db_url}}" -path ./migrations up
# Rollback one migration
migrate-down:
migrate -database "{{db_url}}" -path ./migrations down 1
# Create new migration
migrate-create name:
migrate create -ext sql -dir ./migrations -seq {{name}}
# Seed development database
seed:
psql "{{db_url}}" < ./scripts/seed.sql
Kubernetes Deployment
namespace := "production"
kubeconfig := env_var_or_default("KUBECONFIG", "~/.kube/config")
# Apply manifests
k8s-apply:
kubectl apply -f k8s/ --namespace {{namespace}}
# Watch rollout status
k8s-status:
kubectl rollout status deployment/myapp --namespace {{namespace}}
# Full deploy flow
k8s-deploy: docker-push
just k8s-apply
just k8s-status
@echo "Deployment complete"
Troubleshooting
Recipe not found:
# Check justfile is in current or parent directory
just --justfile /path/to/justfile list
# Just searches upward for justfile automatically
# Name must be exactly 'justfile' or 'Justfile' or '.justfile'
Argument parsing errors:
# Arguments with spaces need quoting
just serve "0.0.0.0:8080"
# Check recipe signature
just --show serve
Environment variable not loading:
# Ensure .env file exists and set dotenv-load is in justfile
ls -la .env
# Debug env loading
just --verbose start
Shebang scripts not executing:
# Ensure the script interpreter is installed
which bash
which python3
# Check shebang line syntax - must be first line of recipe body
# #!/usr/bin/env bash must have no leading spaces
Conclusion
Just provides a simple, Rust-powered command runner that eliminates Makefile's complexity while keeping project automation straightforward and readable. Its support for arguments, variables, environment files, and cross-platform detection makes it ideal for polyglot projects and teams. With a single justfile at your project root, all common tasks are discoverable and consistent across development, testing, and deployment workflows.


