SSH Commander creates a Home Assistant button for each allow-listed SSH command. A button opens a short-lived SSH connection, runs exactly the configured command, captures a bounded result, and disconnects.
It is designed for small homelab actions such as restarting a service, waking a maintenance script, shutting down a machine, or requesting a backup without exposing arbitrary shell execution to automations.
- Password, RSA, ECDSA, and Ed25519 private-key authentication
- Explicit SHA256 host-key confirmation and pinning for new or edited machines
- Add, edit, and remove machines and commands through Home Assistant's reconfigure flow
- One device per SSH machine and one button entity per command
- Optional
ssh_commander.run_commandresponse data for scripts and automations - Hard command timeout, simultaneous stdout/stderr draining, and 8 KiB capture limits
- Duplicate-trigger protection for the same command
- Privacy-safe diagnostics and automatic cleanup of removed command entities
- Automatic migration from the original v1 format, including recovery of changes saved by its broken options flow
- Home Assistant 2026.3.0 or newer
- An SSH server reachable from the Home Assistant host
- Non-interactive remote commands
- Open HACS.
- Add
https://github.com/PolarRaccoon/ha-ssh_commanderas a custom Integration repository. - Install SSH Commander.
- Restart Home Assistant.
Copy custom_components/ssh_commander into your Home Assistant configuration directory:
/config/custom_components/ssh_commander
Restart Home Assistant afterward.
-
Go to Settings → Devices & services → Add integration.
-
Search for SSH Commander.
-
Add a machine and its credentials.
-
Compare the displayed SHA256 host-key fingerprint with the matching key on the server:
for key in /etc/ssh/ssh_host_*_key.pub; do ssh-keygen -lf "$key" -E sha256 done
-
Confirm the key, add at least one command, then choose Save and finish.
Use Reconfigure on the integration entry to add, edit, or remove machines and commands. Changes are staged until Save and finish is selected.
Each command is a normal button entity and can be placed on a dashboard or called from an automation:
action: button.press
target:
entity_id: button.media_server_restart_jellyfinThe button attributes retain the last bounded result:
successreturn_codestdoutandstderrduration_seconds- truncation flags
command_idandmachine_id
The integration action runs only a command already stored in the integration. It does not accept arbitrary shell text.
action: ssh_commander.run_command
data:
entry_id: 01J_EXAMPLE_ENTRY_ID
command_id: 574ae70d-dca6-4497-987d-566e53276f6e
response_variable: ssh_resultFind the stable command_id and config-entry ID in the command button's attributes and integration details. entry_id can be omitted when the command ID is unique across loaded SSH Commander entries.
SSH Commander is intentionally an allow-list, but every configured command still has the permissions of its remote account.
- Create a dedicated, unprivileged SSH user for Home Assistant.
- Prefer a dedicated private key over a password.
- Restrict
sudoto exact commands and absolute paths. Do not grant unrestricted passwordless sudo. - Do not place secrets directly in command strings; results can be recorded in entity history.
- Protect Home Assistant's
.storagedata and backups. Connection credentials are stored in the config entry and are not independently encrypted by this integration. - Verify host-key fingerprints out of band. A changed key is blocked until the machine is explicitly edited and the new fingerprint confirmed.
Example narrow sudoers rule:
ha-ssh ALL=(root) NOPASSWD: /usr/bin/systemctl restart jellyfin.service
The matching configured command would be:
sudo /usr/bin/systemctl restart jellyfin.service- A fresh SSH connection is used for every invocation. This is slightly slower than pooling, but avoids stale sessions and keeps idle resource use at zero.
- Interactive prompts and commands requiring a TTY are unsupported. Configure key authentication or narrowly scoped
NOPASSWDrules. - The configured timeout is applied to each SSH connection phase and to remote command execution.
- Stdout and stderr are each capped at 8 KiB. Extra data is drained to avoid SSH channel deadlocks but is not retained.
- A non-zero remote exit status is a completed result with
success: false; connection and timeout failures raise a Home Assistant action error.
Version 2 migrates existing machines and commands automatically. If the old configuration menu saved newer values into entry.options, those values take precedence during migration so the formerly ignored edits are recovered.
Existing machines do not have a pinned host key. Reconfigure and save each legacy machine once to test the connection, confirm its fingerprint, and enable pinning.
SSH Commander deliberately disables the Home Assistant process's SSH agent and local key search. Paste the intended private key into the integration or configure its password explicitly.
Commands are non-interactive. Use a command-specific NOPASSWD sudoers rule; do not use sudo -S or embed passwords.
Confirm why the remote key changed first. If the machine was rebuilt or its SSH host keys were intentionally rotated, edit that machine in Reconfigure, verify the new fingerprint, and save.
