A Home Assistant add-on that pulls configuration from a git repository and intelligently reloads only the affected domains instead of performing a full restart. Supports SOPS-encrypted secrets and webhook triggers.
- Selective reload — only reloads domains whose files changed (automations, scripts, scenes, themes, etc.)
- Full restart fallback — for
configuration.yaml,secrets.yaml,packages/,custom_components/, or unknown files - SOPS integration — decrypts
secrets.enc.yaml→secrets.yamlafter each pull using age encryption - Webhook + polling — GitHub can trigger immediate sync via POST, plus regular polling interval
- Config validation — checks config before any reload/restart
- Local changes protection — if someone edits config via the HA UI, changes are saved to a branch and a persistent notification is raised
- Clone this repo into
/addons/on your HA host:cd /addons git clone git@github.com:tobydoescode/ha-app-git-sync.git - In HA: Settings → Add-ons → Add-on Store → ⋮ (top right) → Check for updates
- Find "Git Sync with Selective Reload" under Local add-ons and install it
| Option | Default | Description |
|---|---|---|
repository |
required | Git repo URL (SSH or HTTPS) |
branch |
main |
Branch to track |
ssh_key |
SSH private key for git auth | |
git_token |
HTTPS token for git auth | |
poll_interval |
300 |
Seconds between polls (30–86400) |
auto_restart |
true |
Restart HA if reload isn't sufficient |
check_config |
true |
Validate config before reload/restart |
restart_on_unknown |
true |
Restart when changed files can't be mapped to a reload |
reload_map |
see below | INI-style file-to-action mapping |
sops_enabled |
false |
Decrypt encrypted files after pull |
sops_age_key_file |
Path to age key file | |
sops_files |
see below | List of encrypted = decrypted file pairs |
webhook_enabled |
false |
Listen for webhook triggers |
webhook_port |
8199 |
Port for webhook listener |
This keeps secrets.yaml out of your git repo. Only the encrypted secrets.enc.yaml is committed.
Run this locally (or anywhere with age installed):
age-keygen -o sops-age-key.txtThis prints a public key like age1abc123.... Save it — you'll need it next.
Copy the key file to your HA host:
scp sops-age-key.txt root@<ha-host>:/config/.sops-age-key.txtcreation_rules:
- path_regex: secrets\.enc\.yaml$
age: "age1abc123..." # your public key from step 1sops -e secrets.yaml > secrets.enc.yaml# Decrypted secrets — never commit
secrets.yaml
.sops-age-key.txt
# HA runtime files
home-assistant_v2.db
home-assistant_v2.db-wal
home-assistant_v2.db-shm
__pycache__/
*.pyc
tts/
.cloud/
.storage/auth*
backups/Set these options in the add-on configuration:
sops_enabled: true
sops_age_key_file: /config/.sops-age-key.txt
sops_files:
- "secrets.enc.yaml = secrets.yaml"
- "esphome/secrets.enc.yaml = esphome/secrets.yaml"The add-on will decrypt each encrypted file to its decrypted counterpart after every pull. Paths are relative to /config/.
Use sops to edit — it decrypts in your editor and re-encrypts on save:
sops secrets.enc.yamlOr edit secrets.yaml directly and re-encrypt:
sops -e secrets.yaml > secrets.enc.yaml# Clone onto HA host
ssh root@<ha-host>
cd /addons
git clone git@github.com:tobydoescode/ha-app-git-sync.gitInstall from HA: Settings → Add-ons → Add-on Store → Local add-ons
Requires Task:
task test # run all tests
task test:unit # unit tests only
task test:integration # integration tests only
task build # build the Docker image
task shell # build + open a shell in the container# On your local machine — make changes, push
git add -A && git commit -m "..." && git push
# On HA host — pull changes
ssh root@<ha-host> "cd /addons/ha-app-git-sync && git pull"Then in HA: Add-on page → ⋮ (three-dot menu, top right) → Rebuild
Check logs: Add-on page → Log tab
If someone edits config through the HA UI (e.g. automations, scripts), the add-on will detect uncommitted changes before syncing and:
- Commit them to a timestamped branch (e.g.
local/changes-20260409-120000) - Attempt to push the branch to the remote
- Raise a persistent notification in the HA UI
- Continue syncing from the main branch
If push fails, the branch is kept locally on the HA host.
The reload_map option controls which files trigger which actions. It uses an INI-style format where sections are actions and lines are glob patterns:
[automation/reload]
automations.yaml
automations/*
[script/reload]
scripts.yaml
scripts/*
[RESTART]
configuration.yaml
secrets.yaml
packages/*
custom_components/*
[IGNORE]
.gitignore
*.mdActions:
[domain/service]— calls the HA reload service (e.g.automation/reload)[RESTART]— triggers a full HA restart[IGNORE]— skips the file entirely
Files not matching any pattern are treated as unknown (restart if restart_on_unknown is true).
To add a custom integration, just add a new section:
[pyscript/reload]
pyscript/*