Go Report Card License Release
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: true
sshot -i inventory.yml playbook.yml
sshot [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.yml
With separate inventory:
sshot -i inventory.yml playbook.yml
Dry-run mode:
sshot -n -v -i inventory.yml playbook.yml
With progress indicators:
sshot --progress -i inventory.yml playbook.yml
With full output:
sshot -f -i inventory.yml playbook.yml
Verbose 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.yml
sshot 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 verification
hosts: - 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 codes
This 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: localhost
The 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: true
The 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_hosts
Solution 2: Disable strict checking (not recommended for production)
ssh_config: strict_host_key_check: false
Check:
- 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