A Go-based configuration management and system bootstrapping tool inspired by Ansible. Single binary, no dependencies.
brew install tackhq/tap/tack # Homebrew
go install github.com/tackhq/tack/cmd/tack@latest # GoOr download from the releases page.
tack run examples/playbooks/setup-dev.yaml --check # preview changes
tack run examples/playbooks/setup-dev.yaml # apply# setup.yaml
name: Setup Development Machine
hosts: localhost
connection: local
tasks:
- name: Install packages
brew:
name: [git, go, ripgrep]
state: present
when: facts.os_family == 'Darwin'
- name: Create projects directory
file:
path: "{{ env.HOME }}/projects"
state: directory- Simple YAML playbooks with Ansible-compatible role structure
- Idempotent modules -- apt, brew, yum, file, copy, command, cron, systemd, template, lineinfile, blockinfile, wait_for, assert
- Cross-platform -- macOS (brew) and Linux (apt, yum, systemd)
- Multiple connectors -- Local, Docker, SSH, AWS SSM with tag-based discovery
- Plan/apply workflow -- preview changes before applying,
--auto-approvefor CI - Parallel execution --
--forks Nfor concurrent multi-host runs - Block/rescue/always -- structured error handling with rollback and guaranteed cleanup
- Task inclusion --
include_taskswith scopedvars:,loop:, conditionalwhen:, circular detection - Tag-based filtering --
--tagsand--skip-tagsfor selective task execution, withalways/neverspecial tags - Variable system -- interpolation, filters, registered outputs, vars_files, vault encryption
- JSON output --
--output jsonfor machine-readable output in CI pipelines - System facts -- OS, arch, network, EC2 metadata (via IMDSv2)
- Remote sources -- run playbooks from git repos, S3, or HTTP URLs
- No dependencies -- single static binary
| Connector | Example | Description |
|---|---|---|
| Local | connection: local |
Run on the current machine (default) |
| Docker | -c docker://container |
Run inside a Docker container |
| SSH | -c ssh://user@host:port |
Connect via SSH; reads ~/.ssh/config |
| SSM | --ssm-tags env=prod |
AWS SSM; tag-based instance discovery, S3 file transfer |
Connection type is auto-detected from flags when not specified. See Connectors docs for full configuration and environment variables.
# Serial (default)
tack run deploy.yaml --hosts web1,web2,web3
# Parallel -- up to 5 hosts at once
tack run deploy.yaml --hosts web1,web2,web3,web4,web5 --forks 5
# With inventory groups
tack run deploy.yaml -i inventory.yaml --hosts webservers --forks 10Output is buffered per-host and flushed in order. Errors on one host don't stop others. Use TACK_FORKS env var for CI.
name: Configure Web Server
hosts: web1
connection: ssh
ssh:
user: deploy
key: ~/.ssh/deploy_key
tasks:
- name: Install nginx
apt:
name: nginx
state: present
- name: Enable nginx
systemd:
name: nginx
state: started
enabled: truetack run server-setup.yaml
tack run server-setup.yaml --hosts web1,web2,web3 --forks 3name: Patch App Servers
connection: ssm
ssm:
region: us-east-1
bucket: my-ssm-transfer-bucket
tags:
env: production
role: app-server
tasks:
- name: Install security updates
apt:
name: "*"
state: latesttack run git@github.com:user/repo.git//path/to/playbook.yaml
tack run https://github.com/user/repo.git@v1.0//playbook.yaml
tack run s3://bucket/path/to/playbook.yamlA playbook is either a sequence of plays (as shown above) or a mapping with
hosts, connection, sudo, vars declared once and a plays: array
underneath. Plays inherit any field they don't set themselves:
hosts: webservers
connection: ssh
sudo: true
plays:
- name: Install
tasks:
- apt: { name: nginx, state: present }
- name: Deploy
tasks:
- copy:
src: ./app.tar.gz
dest: /opt/app/Play-level values always win on conflict. See examples/playbook-defaults.yaml.
Use include_tasks to include shared task files from your playbooks. This eliminates YAML duplication and enables reusable task libraries.
tasks:
# Basic include
- name: Setup common packages
include_tasks: tasks/common.yml
# Include with scoped variables
- name: Install nginx
include_tasks: tasks/install-package.yml
vars:
package_name: nginx
version: "1.24"
# Conditional include
- name: Debian-specific setup
include_tasks: "{{ facts.os_family }}/packages.yml"
when: facts.os_family == "Debian"
# Loop-driven include
- name: Configure services
include_tasks: tasks/configure-service.yml
loop:
- nginx
- redis
- postgres
loop_var: service_nameinclude: and include_tasks: are equivalent -- include_tasks: is the preferred form for consistency with Ansible conventions. Tasks are loaded and executed at runtime, supporting when: conditions, variable-interpolated paths, and loop: iteration.
Variables passed via vars: are scoped to the included tasks and do not persist after the include completes. Registered variables from included tasks do persist.
Circular includes are detected and reported with a clear error chain. Maximum nesting depth is 64.
See examples/include-tasks/ for a complete example.
Group tasks with structured error handling -- attempt a block, run rescue on failure, and always run cleanup:
tasks:
- name: Deploy with rollback
block:
- name: Pull latest code
command:
cmd: git -C /opt/app pull origin main
- name: Run migrations
command:
cmd: /opt/app/migrate.sh
- name: Restart service
command:
cmd: systemctl restart app
rescue:
- name: Rollback code
command:
cmd: git -C /opt/app reset --hard HEAD~1
- name: Restart previous version
command:
cmd: systemctl restart app
always:
- name: Send deploy notification
command:
cmd: /opt/app/notify.shExecution flow:
block:tasks run sequentially. If all succeed,rescue:is skipped.- If any
block:task fails, remaining block tasks stop andrescue:runs. always:runs regardless of block/rescue outcome.- If
rescue:succeeds, the block is considered recovered (no error propagated).
Block-level directives:
when:gates the entire block (including rescue and always)sudo:is inherited by all tasks within block/rescue/always unless overriddenname:provides descriptive output in plan and execution- Blocks can be nested (block within rescue, etc.)
See examples/block-rescue/ for a complete example.
Selectively run or skip tasks using tags:
tack run deploy.yaml --tags deploy # only deploy-tagged tasks
tack run deploy.yaml --skip-tags debug # skip debug tasks
tack run deploy.yaml --tags deploy,config # OR logic: deploy or config
tack run deploy.yaml --check --tags deploy # plan mode respects tagsTags can be applied to tasks, blocks, plays, and role references:
name: Deploy
hosts: webservers
tags: [infra] # play-level: inherited by all tasks
roles:
- role: webserver
tags: [web] # role-level: inherited by all role tasks
tasks:
- name: Install nginx
apt:
name: nginx
tags: [packages, web]
- name: Deploy block
tags: deploy # block-level: inherited by child tasks
block:
- name: Pull code
command:
cmd: git pull
- name: Restart
command:
cmd: systemctl restart appSpecial tags:
always-- task runs even when--tagsfilter is active (unless explicitly in--skip-tags)never-- task is skipped by default, runs only when explicitly included via--tags
Tag inheritance: A task's effective tags are the union of its own tags plus inherited tags from its play, role, and enclosing block(s). Tags are additive.
Handlers: Notified handlers always run regardless of --tags, but respect --skip-tags.
See examples/playbooks/tags-demo.yaml for a complete example.
Define hosts, groups, SSH config, and variables in a reusable file:
# inventory.yaml
hosts:
web1:
ssh: { user: deploy, key: ~/.ssh/id_deploy }
vars: { region: us-east-1 }
web2:
ssh: { user: deploy }
groups:
webservers:
hosts: [web1, web2]
vars: { app_port: 8080 }tack run deploy.yaml -i inventory.yaml --hosts webserversTack supports dynamic inventory sources beyond static YAML files. If -i points to an executable, Tack runs it with --list and parses the JSON/YAML output. Plugin-based sources are configured via YAML files with a plugin: key.
Script inventory -- any executable that outputs Tack inventory format:
tack run deploy.yaml -i ./my-inventory-script.shHTTP inventory -- fetch from a REST API:
# cmdb-inventory.yml
plugin: http
url: https://cmdb.example.com/api/inventory
headers:
Authorization: "Bearer {{ env.CMDB_TOKEN }}"EC2 inventory -- discover AWS instances by tags:
# ec2-inventory.yml
plugin: ec2
regions: [us-east-1, us-west-2]
filters:
tag:env: production
group_by: [tag:role, tag:env]
host_key: private_ip # or instance_id (for SSM), public_ipMultiple sources -- merge inventories with multiple -i flags:
tack run deploy.yaml -i ec2-inventory.yml -i overrides.ymlInspect inventory -- debug resolved hosts and groups:
tack inventory --list -i ec2-inventory.yml
tack inventory --host web1 -i inventory.yamlSee examples/dynamic-inventory/ for complete samples and examples/inventory.yaml for a static inventory sample.
| Module | Description |
|---|---|
apt |
Manage packages on Debian/Ubuntu |
brew |
Manage Homebrew packages on macOS |
yum |
Manage packages on RHEL/CentOS/Fedora (auto-detects dnf) |
command |
Execute shell commands |
copy |
Copy files or write inline content |
cron |
Manage cron entries idempotently (Linux user crontabs and /etc/cron.d drop-ins) |
file |
Manage files, directories, and symlinks |
git |
Check out git repositories idempotently by branch/tag/SHA |
lineinfile |
Ensure a specific line is present or absent in a file |
blockinfile |
Manage a block of text between marker lines in a file |
systemd |
Manage systemd services (start, stop, enable, mask, daemon-reload) |
template |
Render Go templates with variable substitution |
user |
Manage system users on Linux (create, modify, remove) |
group |
Manage system groups on Linux (create, modify, remove) |
wait_for |
Wait for port, path, command, or URL before proceeding |
assert |
Validate preconditions (built-in keyword); fail fast with a clear message |
Run tack module <name> for detailed parameter docs.
tack generate --packages nginx --files /etc/nginx/nginx.conf -c ssh://root@web1 # capture live state
tack scaffold myrole # create role boilerplate
tack test myrole # test in Docker container (reused for idempotency checks)
tack validate playbook.yaml # syntax check
tack vault init secrets.yaml # create encrypted vault file| Document | Description |
|---|---|
| Getting Started | Installation, first playbook, CLI reference |
| Playbooks | Tasks, handlers, loops, conditionals |
| Roles | Reusable role structure |
| Modules | All modules with parameters and examples |
| Variables & Facts | Interpolation, filters, system/network/EC2 facts |
| Connectors | Local, Docker, SSH, SSM configuration |
| Development | Building, testing, project structure |
| llms.txt | LLM-optimized reference (for AI code generation) |
MIT License