Every project ends up with a handful of commands everyone needs to remember: how to install dependencies, run the build, run the tests, start the dev server, cut a release. Those commands usually live in a README, a pile of shell scripts, or a `package.json` scripts block that only works for Node projects. `just` gives them a proper home.

**just** is a command runner — a single tool for saving and running project-specific commands from a file called a `justfile`. If you've reached for a Makefile to do this and fought with tabs, phony targets, and shell quirks, just is the purpose-built alternative: the same "run a named recipe" convenience, without the baggage.

## What just is (and isn't)

just is **not** a build system. It won't track file timestamps or decide what needs recompiling the way `make` does. It does one thing well: it stores commands as named *recipes* and runs them. That narrow focus is the point — it's a friendlier, more predictable replacement for the way most teams (ab)use Makefiles as task runners.

It's a single Rust binary, works the same on macOS, Linux, and Windows, and is language-agnostic: the recipes inside can run anything.

## Installing just

```bash
# macOS / Linux
brew install just

# Windows
winget install --id Casey.Just

# Any platform with Rust
cargo install just
```

## Your first justfile

Create a file named `justfile` in your project root:

```makefile
# install dependencies
install:
    npm ci

# build the app (runs install first)
build: install
    npm run build

# run the test suite (runs build first)
test: build
    npm test
```

Now run recipes by name:

```bash
just build        # runs install, then build
just test         # runs install, build, then test
just              # runs the first recipe (or the default)
just --list       # show every available recipe
```

The `test: build` syntax declares a dependency: asking for `test` runs `build` first, which runs `install` first. You describe *what depends on what* once, and just resolves the order.

## Recipes that take arguments

Recipes can accept parameters, which makes them far more flexible than a static script:

```makefile
# deploy to a named environment
deploy target:
    ./scripts/deploy.sh {{target}}
```

```bash
just deploy staging
just deploy production
```

You can also define variables and reuse them:

```makefile
node_env := "production"

build:
    NODE_ENV={{node_env}} npm run build
```

## Loading environment variables

just can load a `.env` file automatically so your recipes have the variables they need, without you exporting them by hand:

```makefile
set dotenv-load

start:
    node server.js    # sees everything from .env
```

Keep real secrets out of a plaintext `.env`, though. For anything sensitive, [encrypt your .env files with dotenvx](https://www.deployhq.com/guides/dotenvx) and decrypt them at runtime rather than committing raw values.

## Recipes in other languages

A recipe isn't limited to shell. With a shebang, it runs in whatever interpreter you name — handy for a quick script that would be awkward in bash:

```makefile
count-files:
    #!/usr/bin/env python3
    import os
    print(len(os.listdir(".")))
```

## Using just with DeployHQ

Here's where just pays off for deployment specifically: **the recipes you run locally are the exact commands your build pipeline should run.** You've already written `just build` to mean `npm ci && npm run build` — so there's a single, version-controlled definition of how your project is built, and no drift between "what I run on my laptop" and "what the server runs."

There are two clean ways to wire that into DeployHQ:

1. **Mirror the commands.** Copy the commands from your recipe into your project's [build pipeline](https://www.deployhq.com/features/build-pipelines) steps. The `justfile` stays the human-friendly local entry point; the pipeline runs the same underlying commands.
2. **Install just as a build step**, then call the recipe directly, so both sides literally run the same recipe:

   ```bash
   cargo install just
   just build
   ```

Either way, once your build commands live in a recipe, enable [automatic deployments](https://www.deployhq.com/features/automatic-deployments) so every push runs that same build and ships the result — the command you tested locally is the command that reaches production.

## just vs a full task runner

If all you need is named commands with dependencies, just is hard to beat for simplicity. If you also want to manage the *runtime versions* those commands run against, or load per-project environment variables, a broader tool may fit better — you can [define tasks alongside runtimes and env with mise](https://www.deployhq.com/guides/mise) in one file. Many teams happily use both: mise for versions and environment, just for the day-to-day command menu.

Ready to run the same build on your server that you run locally? [Create a free DeployHQ account](https://www.deployhq.com/signup) and connect your repository in minutes.