Run your own server scripts from Home Assistant automations
Let Home Assistant start a LAN-server job
MQTT suits continuing state updates, while the REST API suits calls from external programs. An automation reverses the direction: a Home Assistant trigger starts a narrowly defined task on the LAN server. Two practical routes are shell_command over SSH and rest_command to an authenticated HTTP endpoint.
Prerequisites
- Home Assistant and the server can reach each other over a trusted LAN or VPN.
- The task already exists as a systemd service.
- SSH uses a dedicated unprivileged account.
- Host keys and credentials are verified and kept out of published YAML.
Understand the HAOS execution context
On Home Assistant OS, shell_command runs inside the homeassistant container—not inside the terminal add-on and not on the HAOS host. The official documentation lists tools such as ssh, curl, and sh; the working directory is /config, and commands are terminated after 60 seconds.
The remote command should therefore only start the job. systemd then runs and logs the long task independently.
1. Prepare keys and restricted sudo
Store a dedicated key under /config/.ssh/ and verify the server host key in known_hosts. Do not disable host-key checking. On the LAN server, grant only the exact command in /etc/sudoers.d/homeassistant-backup, verified with visudo -f:
homeassistant ALL=(root) NOPASSWD: /usr/bin/systemctl start backup.service
The service itself must use fixed paths and must not execute unchecked parameters.
2. Define the shell command
shell_command:
start_lanserver_backup: >-
ssh -i /config/.ssh/id_ed25519_lanserver
-o BatchMode=yes
-o ConnectTimeout=10
-o UserKnownHostsFile=/config/.ssh/known_hosts
homeassistant@SERVER-IP
'sudo /usr/bin/systemctl start backup.service'
Reload Shell Command in Developer tools → YAML or call shell_command.reload.
3. Add the automation
automation:
- alias: "Start nightly backup on the LAN server"
triggers:
- trigger: time
at: "02:00:00"
conditions: []
actions:
- action: shell_command.start_lanserver_backup
response_variable: backup_start
- if:
- condition: template
value_template: "{{ backup_start.returncode != 0 }}"
then:
- action: notify.notify
data:
message: "The backup service could not be started: {{ backup_start.stderr }}"
The response reports the short SSH call, not the eventual backup result. Publish completion separately through MQTT, a status file, or a dedicated endpoint.
REST alternative
An HTTP endpoint can be easier to isolate, but it must authenticate requests, accept only POST, validate all input, and use HTTPS or a trusted network/VPN:
rest_command:
start_lanserver_backup:
url: "https://lanserver.example.lan/api/backup"
method: POST
headers:
Authorization: "Bearer {{ token }}"
content_type: "application/json"
payload: '{"job":"backup"}'
Do not publish the token or replace this with an unauthenticated HTTP script.
Troubleshooting
1. Test in the actual shell_command runtime environment.
2. Check key and known_hosts permissions.
3. Inspect journalctl -u ssh and journalctl -u backup.service on the server.
4. Remember the 60-second command limit.
5. For REST, inspect the HTTP status, certificate, and server log.