Open-source project
umputun/spot avatar
umputun/spot

Spot: a single binary task runner for deploying to remote hosts

A user-friendly and efficient tool for the effortless deployment and configuration of resources on remote machines.

549 stars56 forksGoMIT

At a glance

What is it?
Spot is a Go tool that runs a playbook of tasks across a list of target hosts concurrently, with built-in commands for scripts, file copies, directory syncs, deletes, echo and wait, and with secrets supplied by a built-in store, HashiCorp Vault, Ansible Vault or AWS Secrets Manager. It ships as one binary with no external dependencies, plus a second binary for the secrets service.
Who is it for?
Adopt Spot if you want to run the same deploy or configuration steps across a handful of hosts without adopting a large automation framework, and you are comfortable expressing your steps as a YAML or TOML playbook with a small fixed set of command types. Do not adopt it if your automation needs a broad plugin ecosystem, conditionals, handlers or a large module library, because the built-in command set is six verbs and the design assumes your playbooks are simple sequences.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 13 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A task runner, not a configuration management system

Spot is described as a tool for the deployment and configuration of resources on remote machines, and the README's own summary is a playbook with a list of tasks and targets, where each task consists of a series of commands that can be executed on remote hosts concurrently.

That sentence defines the whole product. There is no agent to install on the target, no inventory database, no server component, and no daemon. Spot opens SSH and SFTP connections from the machine you run it on, and everything it does is driven by the playbook file you wrote.

The feature list in the README is arranged in a way that tells you the shape of the tool. You define tasks with a list of commands and a list of target hosts. You can specify remote hosts directly or through inventory files or inventory URLs. Everything can be defined in a simple YAML or TOML file. Scripts can be run on remote hosts and on the localhost. There are built-in commands, and the README names them: script, copy, sync, delete, echo and wait. Execution is concurrent across the hosts of a task. You can wait for a specific condition before executing the next command. Environment variables are customizable. You can override the list of destination hosts, the SSH username and the SSH key file. You can skip commands or execute only specific ones. Errors can be caught and a command hook run on the local host. There is a debug mode that prints the commands, the output and the other details, and a dry-run mode that prints the commands without executing them. There is an ad-hoc mode for running a single command on a list of hosts. Tasks can be imported from external files to organise large playbooks. And the whole thing is a single binary with no external dependencies.

Two of those deserve emphasis because they are the ones that change how you work. The dry-run mode is the difference between a deployment tool you can try and one you cannot, because it lets you see the exact command set a playbook produces on a given target set before it touches a machine. The error hook is the difference between a tool that stops and one that reports, because it lets a failed deployment trigger a local command, such as a notification, at the point of failure.

The tool is written in Go, the module is github.com/umputun/spot, and the topics are automation, deployment, devops, sftp, ssh and task-runner, which is an accurate description of the mechanism.

The playbook model: tasks, commands and targets

The unit of work is a task, and a task is a list of commands plus the hosts to run them on. The three are selected on the command line rather than only in the file, which is what makes the same playbook reusable across environments.

The options section of the README shows how. The playbook file is chosen with -p or --playbook, which defaults to spot.yml and can also be set through the environment variable $SPOT_PLAYBOOK. The task is chosen with -n or --task, and the behaviour there is worth reading closely: Spot first tries to match an exact task name, and if no match is found it treats the value as a tag and runs all tasks with that tag in playbook order. Several values can be given by repeating the flag.

That tag fallback is the mechanism that makes a playbook usable at more than one level of granularity. A task named deploy-things is exact; a task tagged deploy is a group of them, and a task tagged all is everything. The order is the playbook order, not the command line order, so the sequence of operations is defined in the file and not by how you typed the flags.

The targets are chosen the same way, with --target, and the screenshots section shows the three forms in practice. A playbook with a named target is run as:

bash
spot -p spot.yml -t prod

the same playbook in dry mode adds the flag:

bash
spot -p spot.yml -t prod --dry

and a single ad-hoc command against two explicit hosts is:

bash
spot "ls -la /tmp" -t dev1.umputun.com -t dev2.umputun.com

The ad-hoc form is the one that changes the feel of the tool. A one-off command against a list of hosts, with the same inventory and connection handling as the playbook path, is the operation people run most often, and having it in the same binary with the same flags means there is no second tool to learn.

The playbook format is YAML or TOML, the repository ships a spot-example.yml at the root, and there is an .examples directory alongside a schemas directory, which suggests the file format is validated rather than parsed hopefully.

Secrets, four ways, and four different trust models

The secrets story is where a deployment tool earns its keep, and Spot offers four providers: a built-in secrets storage, HashiCorp Vault, Ansible Vault and AWS Secrets Manager.

The built-in one is the reason there is a second binary. The homebrew install note says that installing the cask installs both the spot and the spot-secrets binaries, and the Makefile builds cmd/spot and cmd/secrets as separate programs. So the built-in provider is a service you run, and spot-secrets is the client and server for it.

The Vault provider is the conventional one, using the HashiCorp Vault API client from the dependency list. The Ansible Vault provider is there for teams whose secrets are already in Ansible Vault format, handled by the ansible-vault-go library, which is a thoughtful touch for anyone migrating from Ansible. AWS Secrets Manager is handled with the AWS SDK for Go v2, split into the config, credentials and secretsmanager modules.

The interesting operational point is that these are not interchangeable, because each one fails differently. A built-in store is something you operate, so it is a service to back up and secure. Vault is a service you operate, and it can be locked or sealed. Ansible Vault is a file format encrypted with a password, so it is available offline and versioned, and it fails when the password or the format handling is wrong. AWS Secrets Manager is a cloud API call per secret, with the SDK's own credential chain deciding whether that call succeeds.

The rest of the dependency list tells you what else the tool talks to. There is a MySQL driver and a PostgreSQL driver, plus a pure Go SQLite, which together with the secrets service suggests the built-in store keeps its state in a database. There is SFTP from the pkg/sftp library, which is the transport for the copy and sync commands. There is a TOML library and the YAML library for the two playbook formats, go-flags for the command line, golang.org/x/crypto for the SSH primitives, and hashicorp/go-multierror, which is how the tool aggregates failures across a concurrent run so that one host failing does not hide the others.

The multierror dependency is the quiet design decision in that list. With concurrent execution, partial failure is the normal case rather than the exception, and a tool that reports only the first error would be actively misleading.

Installing it: a binary, a package, or go install

The primary route is to download the latest release from the Releases page, and the README's other install methods are worth comparing because they differ in what they assume about your machine.

The homebrew cask is the shortest for macOS:

bash
brew tap umputun/apps
brew install --cask umputun/apps/spot

and the note under it says this installs both the spot and the spot-secrets binaries.

The Linux packages are deb, rpm and apk, each fetched with wget from the release assets and installed with the platform's package manager, so the same pattern covers Ubuntu and Debian, CentOS, RHEL, Fedora and AWS Linux, and Alpine.

The universal script works on Linux and macOS. The README's own guidance is to download it and review it first:

bash
wget https://raw.githubusercontent.com/umputun/spot/master/install.sh
sudo sh install.sh

It detects the operating system and architecture and downloads the correct binary. The README does offer the pipe-to-shell form for the brave, but recommends against it, and that recommendation matches the way the tool itself is meant to be used: you should know what you are about to run on a machine that holds deployment credentials.

The last option needs Go on your system and installs both binaries straight from the module:

bash
go install github.com/umputun/spot/cmd/spot@latest
go install github.com/umputun/spot/cmd/spot-secrets@latest

Two details about the distribution are worth noticing. First, the release binaries are produced with goreleaser, and the .goreleaser.yml in the tree is the configuration, which is how a single Go project ends up with deb, rpm and apk packages and macOS builds. Second, the module declares go 1.26.0 and the repository vendors its dependencies, with a vendor directory at the root, which keeps the build reproducible.

The Makefile also stamps a revision string into the binary at build time, derived from the git tag or branch, the short commit hash and a timestamp, so the version you run tells you which tree it came from.

Debugging, dry runs and the local error hook

Three mechanisms decide how confidently you can operate a deployment tool, and all three are in the feature list.

The dry-run mode prints the commands to be executed without executing them. Combined with the target selection, that gives you a way to answer the question that actually matters before a change, which is what exactly would run on those hosts. The README's own screenshots pair a normal run and a dry run of the same playbook, which suggests the author expects you to compare them.

The debug mode goes further, printing the commands to be executed, the output of the commands, and all the other details. That is a different tool from dry run, because it is for after the fact, when you need to see what a command actually returned rather than what it would have returned.

The error hook is the third, and it is the one with the most operational consequence. The feature list says you can catch errors and execute a command hook on the local host. The hook runs locally, which is right: a failed deployment on five hosts should trigger a notification from the machine that ran the deployment, not from the host that failed, and a script that runs where the failure happened is a script that might not run at all.

The wait command completes the picture. You can wait for a specific condition before executing the next command, which is the mechanism behind the rolling update mentioned in the README's feature list, where concurrent execution of a task on multiple hosts is supported. The distinction Spot draws between concurrent and rolling is worth understanding before you rely on it, because concurrent means all hosts at once, and rolling means a subset at a time with a wait between batches. For a change that takes a service down for a minute, those are not the same operation, and the wait condition is what makes the difference safe.

Two smaller conveniences complete the feature set: you can skip commands or execute only specific ones, and you can override the list of destination hosts, the SSH username and the SSH key file at the command line, which is what makes a single playbook usable against staging and production without editing the file.

Where Spot sits against Ansible, and what it costs to adopt

The obvious comparison is Ansible, and the fact that Spot ships an Ansible Vault secrets provider is itself a comment on how the two tools relate.

The difference in approach is mostly one of scope. Spot has a fixed vocabulary of six built-in commands, script, copy, sync, delete, echo and wait, and a playbook is a sequence of those applied to a list of hosts. Ansible has a module library, roles, conditionals, handlers, templates, tags and a plugin ecosystem, and a playbook is a program that can branch. The trade runs in both directions: Spot is a small Go binary that you can read in an afternoon and reason about completely, while Ansible is a large framework whose power is exactly the thing that makes it hard to hold in your head.

There is also a difference in what the tool assumes about the target. A task runner that opens connections and runs commands expects the SSH path to work and the remote host to have the tools the commands need, which is why the built-in commands are shell level rather than module level. It is the same shape as a Makefile that runs over SSH, and that is the mental model that fits.

The project is maintained by a single author, umputun, and the repository reflects a mature solo project rather than a committee. There is a Makefile with a test target that runs the suite with the race detector and a coverage profile, and then filters out the generated mocks before reporting coverage, which is a sign of someone who cares about the number meaning something. There is a .golangci.yml for linting, a .goreleaser.yml for the release pipeline, a completions directory for shell completion, a man page at spot.1, a schemas directory, a docs directory and a site directory with the documentation source, plus a CLAUDE.md. The recent releases are v1.20.1 from 2026-07-15, v1.20.0 from 2026-02-18 and v1.19.3 from 2026-01-14, and the last push to the repository was on 2026-10-10.

The licence is MIT, with the LICENSE file at the root of the repository, so there is no copyleft question if you want to modify the tool or embed its logic in your own pipeline.

So the summary for an evaluator is that Spot is deliberately a smaller thing than Ansible, and its value is that it is a small thing. If your deployment story is a sequence of the same steps across a list of hosts, it is a good fit, and the dry run plus the local error hook are the two features that make it safe to run. If your deployment story is a branching program with a dozen different module types, you will outgrow it, and the README's own feature list is the place to notice that coming.

Editorial conclusion

Adopt Spot if you want to run the same deploy or configuration steps across a handful of hosts without adopting a large automation framework, and you are comfortable expressing your steps as a YAML or TOML playbook with a small fixed set of command types. Do not adopt it if your automation needs a broad plugin ecosystem, conditionals, handlers or a large module library, because the built-in command set is six verbs and the design assumes your playbooks are simple sequences. Verify first which secrets provider you will use, since the built-in store, Vault, Ansible Vault and AWS Secrets Manager are four different trust models and four different failure modes, that your inventory is reachable from the machine running spot, whether as a file or as a URL, and that the wait command's condition mechanism matches how your services actually signal readiness, because a rolling update that waits for the wrong thing is worse than one that does not wait at all.

Frequently asked questions

What is Spot?

Spot is a Go tool for deployment and configuration management on remote machines. You define a playbook of tasks and targets in YAML or TOML, and each task is a series of commands executed concurrently on the target hosts over SSH and SFTP.

How do I install Spot?

Download the latest release binary from the Releases page, install the homebrew cask with brew tap umputun/apps and brew install --cask umputun/apps/spot, use the deb, rpm or apk package for your distribution, run the reviewed install.sh script, or install from source with go install github.com/umputun/spot/cmd/spot@latest. The homebrew and go install routes bring in the spot-secrets binary as well.

Which commands does Spot support?

The built-in command types are script, copy, sync, delete, echo and wait. You can also skip commands or run only specific ones, override the destination hosts, SSH username and SSH key file from the command line, and import tasks from external files.

How does Spot handle secrets?

It supports a built-in secrets storage, HashiCorp Vault, Ansible Vault and AWS Secrets Manager. The built-in provider is served by the separate spot-secrets binary, and the dependency list includes the Vault API client, an Ansible Vault library and the AWS SDK for Go v2 secretsmanager module.

What are the dry run and debug modes in Spot?

Dry run prints the commands that would be executed without executing them, which lets you see what a playbook produces on a given set of targets before it touches a machine. Debug mode prints the commands, the output of the commands and the other details, so it is for inspecting a run rather than previewing one.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. umputun/spot on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/umputun-spot.svg)](https://hysenlabs.com/projects/umputun-spot)

Was this useful?