Orchestrate SSH operations with ease
sshot (SSH Orchestrator Tool) is a lightweight, Ansible-inspired tool for sysadmins who need straightforward SSH orchestration without Python dependency headaches. Built with Go for portability and simplicity, it uses familiar YAML playbooks—perfect for daily administrative tasks.
If you're a sysadmin who loves Ansible's YAML approach but sometimes finds Python dependencies challenging, sshot might be for you.
sshot is NOT a replacement for Ansible - it doesn't try to be. Ansible is a comprehensive automation platform with an extensive ecosystem. sshot is simply a focused helper tool for sysadmins who need straightforward SSH orchestration.
- 🪶 No Python headaches - Single Go binary, no dependencies, no virtualenvs, no pip issues
- 🎯 Sysadmin-focused - Built for daily SSH tasks, not enterprise-wide automation
- ⚡ Portable - Copy one binary, run anywhere (Linux, macOS, even on edge devices)
- 📝 Familiar syntax - If you know Ansible YAML, you already know sshot
- 🚀 Fast - Go's performance for quick task execution
go install github.com/fgouteroux/sshot@latest# Download from GitHub releases
wget https://github.com/fgouteroux/sshot/releases/latest/download/sshot_Linux_x86_64.tar.gz
tar xzf sshot_Linux_x86_64.tar.gz
sudo mv sshot /usr/local/bin/git clone https://github.com/fgouteroux/sshot.git
cd sshot
go build -o sshot# inventory.yml
ssh_config:
user: admin
key_file: ~/.ssh/id_rsa
port: 22
hosts:
- name: web1
address: 192.168.1.10
- name: web2
address: 192.168.1.11# playbook.yml
name: Deploy Application
tasks:
- name: Update system
command: apt-get update
sudo: true
- name: Install nginx
command: apt-get install -y nginx
sudo: true
- name: Start nginx
command: systemctl start nginx
sudo: truesshot -i inventory.yml playbook.ymlsshot [options] <playbook.yml>-i, --inventory <file>- Inventory file (supports separate files)-n, --dry-run- Run in dry-run mode (simulate without executing)-v, --verbose- Enable verbose logging--progress- Show progress indicators-f, --full-output- Show complete command output without truncation--no-color- Disable colored output
Basic execution:
sshot playbook.ymlWith separate inventory:
sshot -i inventory.yml playbook.ymlDry-run mode:
sshot -n -v -i inventory.yml playbook.ymlWith progress indicators:
sshot --progress -i inventory.yml playbook.ymlWith full output:
sshot -f -i inventory.yml playbook.ymlVerbose with full output:
sshot -v -f playbook.yml$ sshot playbook.yml
┌─ Host: server1 (192.168.1.10)
│
│ [1/1] Check logs
✓ Success
Output (showing first 5 and last 5 lines of 100 total):
Line 1
Line 2
Line 3
Line 4
Line 5
... (90 lines omitted) ...
Line 96
Line 97
Line 98
Line 99
Line 100$ sshot --full-output playbook.yml
# or
$ sshot -f playbook.yml
┌─ Host: server1 (192.168.1.10)
│
│ [1/1] Check logs
✓ Success
Output: (100 lines)
Line 1
Line 2
Line 3
...
Line 100# Dry-run with full output
$ sshot -n -f playbook.yml
# Verbose with full output
$ sshot -v -f playbook.yml
# Full output without color
$ sshot -f --no-color playbook.ymlsshot borrows Ansible's excellent design philosophy but focuses specifically on sysadmin needs:
- ✅ YAML playbooks for configuration
- ✅ Inventory files for host management
- ✅ Task execution with dependencies
- ✅ Conditional task execution (
when) - ✅ Variable substitution
- ✅ Parallel and sequential execution
- ✅ Group-based orchestration
- Parallel execution across multiple hosts
- Sequential execution with ordered groups
- Group dependencies for complex workflows
- Commands - Execute shell commands
- Scripts - Upload and run local scripts
- File copy - Copy files with permissions
- Wait conditions - Wait for ports, services, files, HTTP endpoints
- Retries - Automatic retry with configurable delays
- Timeouts - Task-level timeout control
- Conditionals - Execute tasks based on variables
- Dependencies - Define task execution order
- Variable substitution - Use variables in commands and files
- Register output - Capture and reuse task output
- SSH key-based authentication
- Password authentication
- SSH agent support
- Per-host authentication override
ssh_config:
user: admin
key_file: ~/.ssh/id_rsa
port: 22
strict_host_key_check: true # Set to false to disable verificationhosts:
- name: server1
address: 192.168.1.10
user: deploy # Override global user
vars:
env: production
app_port: "8080"groups:
- name: databases
order: 1
parallel: false
hosts:
- name: db1
address: 192.168.1.20
vars:
role: master
- name: db2
address: 192.168.1.21
vars:
role: slave
- name: webservers
order: 2
parallel: true
depends_on: [databases] # Wait for databases group
hosts:
- name: web1
address: 192.168.1.30
- name: web2
address: 192.168.1.31{% raw %}
name: Multi-tier Deployment
parallel: false # Global parallel setting
tasks:
- name: Check connectivity
command: echo "Connected to {{ .hostname }}"
- name: Install packages
command: apt-get install -y nginx mysql-client
sudo: true
retries: 3
retry_delay: 5
- name: Copy configuration
copy:
src: ./nginx.conf
dest: /etc/nginx/nginx.conf
mode: "0644"
sudo: true
- name: Start service
command: systemctl start nginx
sudo: true
wait_for: port:80
- name: Health check
command: curl -f http://localhost/health
retries: 5
retry_delay: 2
until_success: true
- name: Production only task
command: deploy-prod.sh
when: "{{ .env }} == production"
register: deploy_output{% endraw %}
- name: Execute command
command: echo "hello world"- name: Install package
command: apt-get install -y nginx
sudo: true{% raw %}
- name: Deploy application
command: deploy {{ .app_name }} --port {{ .app_port }}{% endraw %}
{% raw %}
- name: Ubuntu specific
command: apt-get update
when: "{{ .os }} == ubuntu"{% endraw %}
- name: Download artifact
command: wget https://example.com/artifact.tar.gz
retries: 3
retry_delay: 5- name: Build application
command: make build
depends_on: [Install Dependencies, Clone Repository]- name: Search for pattern
command: grep "error" /var/log/app.log
allowed_exit_codes: [0, 1] # 0 = found, 1 = not found
register: search_result
- name: Compare files
command: diff file1.txt file2.txt
allowed_exit_codes: [0, 1] # 0 = identical, 1 = different
- name: Custom script
command: ./check_status.sh
allowed_exit_codes: [0, 2, 3] # Multiple allowed codesThis is useful for commands like grep, diff, or custom scripts where
non-zero exit codes have specific meanings that should still be considered
successful. If a command exits with a code in the allowed list, it will
be treated as successful and won't trigger retries or fail the playbook.
- name: Copy config
copy:
src: local/config.yml
dest: /etc/app/config.yml
mode: "0644"
sudo: true- name: Run setup script
script: ./scripts/setup.sh
sudo: true- name: Wait for database
wait_for: port:5432
- name: Wait for service
wait_for: service:postgresql
- name: Wait for file
wait_for: file:/var/run/app.pid
- name: Wait for HTTP
wait_for: http://localhost:8080/health- name: Run locally
local_action: echo "Running on the local machine"Local actions run commands on the local machine rather than on the remote hosts.
- name: Run on specific host
command: echo "Running delegated command"
delegate_to: db-server
- name: Run locally with delegation
command: echo "Running locally via delegation"
delegate_to: localhostThe delegate_to option allows running a command on a specific host, rather than all hosts in the inventory or group.
- name: Database schema update
command: ./update-schema.sh
run_once: true
- name: Local notification
local_action: ./send-notification.sh "Deployment started"
run_once: trueThe run_once flag ensures a task is only executed once, even if multiple hosts are targeted. This is particularly useful for database migrations, notifications, or other actions that should happen only once during a playbook run.
These features can be combined:
- name: Initialize application
command: ./init-app.sh
delegate_to: app-primary
run_once: true# inventory.yml
ssh_config:
user: deploy
key_file: ~/.ssh/deploy_key
hosts:
- name: prod-server
address: production.example.com
# playbook.yml
name: Deploy Website
tasks:
- name: Pull latest code
command: git pull origin main
- name: Install dependencies
command: npm install
- name: Build application
command: npm run build
- name: Restart service
command: systemctl restart app
sudo: true{% raw %}
# inventory.yml
ssh_config:
user: admin
key_file: ~/.ssh/id_rsa
groups:
- name: database
order: 1
hosts:
- name: db-primary
address: 10.0.1.10
vars:
role: primary
- name: db-replica
address: 10.0.1.11
vars:
role: replica
- name: application
order: 2
parallel: true
depends_on: [database]
hosts:
- name: app1
address: 10.0.2.10
- name: app2
address: 10.0.2.11
- name: loadbalancer
order: 3
depends_on: [application]
hosts:
- name: lb1
address: 10.0.3.10
# playbook.yml
name: Deploy Multi-tier Application
tasks:
- name: Stop application
command: systemctl stop myapp
ignore_error: true
- name: Backup database
command: pg_dump mydb > /backup/mydb.sql
when: "{{ .role }} == primary"
sudo: true
- name: Update application
command: deploy.sh --version {{ .version }}
vars:
version: "2.0.0"
retries: 3
- name: Start application
command: systemctl start myapp
sudo: true
- name: Wait for service
wait_for: port:8080
- name: Health check
command: curl -f http://localhost:8080/health
retries: 10
retry_delay: 3{% endraw %}
Error:
host key verification failed for hostname: knownhosts: key is unknown
To add this host, run: ssh-keyscan -H hostname >> /home/user/.ssh/known_hosts
Solution 1: Add the host key
ssh-keyscan -H hostname >> ~/.ssh/known_hostsSolution 2: Disable strict checking (not recommended for production)
ssh_config:
strict_host_key_check: falseCheck:
- SSH key permissions:
chmod 600 ~/.ssh/id_rsa - SSH key path is correct in inventory
- User has SSH access to the host
- Try manual SSH:
ssh user@host
Solutions:
- Check host is reachable:
ping hostname - Verify port is correct (default: 22)
- Check firewall rules
- Verify SSH service is running:
systemctl status sshd
Contributions are welcome! Please feel free to submit a Pull Request.
# Clone repository
git clone https://github.com/fgouteroux/sshot.git
cd sshot
# Install dependencies
go mod download
# Run tests
make test
# Build
make build
# Run linter
make lint# All tests
go test -v ./...
# With coverage
go test -cover ./...
# Specific package
go test -v ./pkg/config/...Apache License 2.0 - see LICENSE file for details.
François Gouteroux
- Inspired by Ansible - for pioneering YAML-based automation
- Built with Go - for performance and simplicity
- Uses golang.org/x/crypto/ssh - for SSH connectivity
sshot - SSH Orchestrator Tool | GitHub | Documentation