TA

Task

Task runner and build tool simpler than Make

Developer Tools & Git ★ 16.2k stars Easy setup MIT

Task is a task runner and build tool that uses a simple YAML file to define and run repeatable commands across projects. It is self-hosted as a single cross-platform binary.

Key features

  • YAML task definitions
  • Cross-platform binary
  • Dependency tracking
  • Variable templating

Pros & cons

Strengths

  • Single static binary
  • Simple YAML syntax
  • Fully cross-platform

Trade-offs

  • Less ubiquitous than Make
  • Complex logic gets awkward

Task replaces

Last reviewed Aug 26, 2026 · 812 words

Every homelab accumulates the same 6 scripts: bring the stack up, pull updates, back up the volumes, restore one, tail the logs, prune what is dangling. Task turns those into one Taskfile.yml per host, discoverable with task --list, runnable with task backup, and delivered as a single static Go binary with zero dependencies on the target machine. It is not a server and there is nothing to host; it is the thing that makes the servers you do host easier to operate.

A homelab Taskfile that earns its place

Here is a real one, trimmed, for a Docker Compose host:

version: '3'

dotenv: ['.env']

vars:
  BACKUP_DIR: /srv/backups

tasks:
  up:
    desc: Start the stack
    cmds:
      - docker compose up -d

  update:
    desc: Pull new images and restart what changed
    cmds:
      - docker compose pull
      - docker compose up -d
      - docker image prune -f

  backup:
    desc: Snapshot volumes to {{.BACKUP_DIR}}
    cmds:
      - restic backup /srv/app-data --tag compose

  logs:
    desc: Follow logs for one service
    cmds:
      - docker compose logs -f {{.CLI_ARGS}}

task logs -- immich passes the service name through CLI_ARGS, desc lines are what task --list prints, and dotenv loads your secrets without you exporting them. That is the whole learning curve for 80 percent of use.

Up-to-date checks and dependencies save real minutes

Task's sources and generates keys give you Make's incremental behaviour without Make's tab-sensitivity: if no listed source file changed since the last run, the task is skipped. On a homelab that turns "rebuild the custom image" into a 0-second no-op most of the time. deps runs prerequisites first, and in parallel by default, so task deploy with deps: [lint, build] does both before the deploy step. task --watch reruns on file change, which is handy while editing a Caddyfile or a Compose file you want validated on each save.

Task against Make and just

TaskMakejust
SyntaxYAMLMakefile, tabs matterMakefile-like, no tabs rule
Installed everywhere alreadyNoYes on Linux and macOSNo
Incremental via file timestampsYesYes, it is the whole pointNo
Windows supportFirst-classPainfulGood
Templating and variablesGo templatesMake's own languageIts own, simpler

Make is ubiquitous and that matters: a Makefile runs on any box you SSH into. Task and just both need to be installed first, which is the catalogue's "less ubiquitous" drawback in one line. Between the two, just has the nicer syntax for plain command lists and Task has incremental builds, includes and a proper deps model. I use Task where the build is real and just where it is a list of aliases.

Where the YAML gets in the way

Complex logic in YAML is miserable, and Task does not pretend otherwise. Multi-line shell with quotes, conditionals and loops ends up with | blocks and Go template escaping that is harder to read than the bash it replaced. The fix is simple: once a task exceeds about 10 lines of shell, move it to scripts/thing.sh and have the task call it. Task then stays what it is good at, a discoverable menu with variables and ordering, and the logic lives in a language built for it. The same applies to preconditions and status checks: use them for "is Docker running", not for business rules.

Install once, run everywhere

The install script drops the binary where you tell it, and it is around 10 MB:

sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin

It is also in Homebrew, Snap, most distro repos, and go install github.com/go-task/task/v3/cmd/task@latest if you have Go. Two features matter for a fleet: task -g runs a global Taskfile from your home directory, so task -g update-all can SSH into each host, and includes lets one Taskfile pull in another, so a shared common.yml in a Git repository gives every host the same backup and update tasks with per-host variables. With a Forgejo instance holding that repo, a new box is bootstrapped with one clone and one task setup.

What I'd do

Install Task on every host, commit a Taskfile.yml beside each docker-compose.yml, and give each one the same 5 tasks: up, update, backup, restore and logs. Push complex steps into scripts the tasks call. Keep Make for anything that has to run on a machine you do not control, and keep just out of it unless you already like it. The win is not speed, it is that 6 months from now task --list tells you how this host is operated without opening a single script. The full range of options in the dev-tools category is wide, but this one costs 10 minutes to adopt and pays back weekly.

Compare Task

1 head-to-head comparisons.

Similar developer tools & git apps