I hope I can explain this correctly; I am only somewhat familiar with docker.
I have a script that I run after Home Assistant OS updates; it updates the alpine operating system with some extra packages and creates three symlinks in /usr/local/bin for three scripts in /config/shell_scripts. Up until now, it’s run perfectly when I run it manually over ssh.
Then I decided to create an automation to run it automatically after an HAOS update, and while the package updates work, the script says those symlinks exist already so it doesn’t create them; they do not exist, HAOS deletes them after an update (and Core updates might too, but I usually update them together so never checked).
After a lot of frustration, I logged into Home Assistant with root on the 22222 port and found it’s checking and finding the previously created symlinks from earlier HAOS updates inside /mnt/data/docker/overlay2/…/usr/local/bin and /var/lib/docker/overlay2/…/usr/local/bin. At least, that’s my guess; googling docker and overlays has been a trip.
So my question: how do I structure the script so that the symlink check is within the docker container’s version of /usr/local/bin so it will create the symlink?
I get the answer is probably super obvious, but I am not seeing it. The only other thing I can think to do is export /homeassistant/shell_scripts to PATH but while that works over ssh, I haven’t tested that running as a shell script service and I really want to automate this process.
Screenshot of full script attached; here’s the short version I’m using for testing. This has been checked with and without variables for paths.
#!/bin/bash
# variables
bin_home="/usr/local/bin"
shell_home="/homeassistant/shell_scripts"
# add packages
pkgs="coreutils figlet iproute2 iw jq procps-ng sed util-linux wireless-tools"
apk add $pkgs
echo "Packages Updated"
ln -s "$shell_home/lib_comp" "$bin_home/lib_comp"
ln -s "$shell_home/ipa_status" "$bin_home/ipa_status"
ln -s "$shell_home/ssh_text" "$bin_home/ssh_text"
echo "Done"
Docker containers are designed to be immutable. The moment they’re stopped and recreated, any changes to them ads thrown out. You’re supposed to add a layer to your Docker image if you want to add command lines and such. That’s why it’ll keep deleting your stuff every time you update.
Some persistent paths are mounted into the container (“volumes”). That’s where your data and config live.
Running the script inside Docker should put it in the right place, but I wouldn’t advice doing it that way. To work around the path issue, maybe consider using hard links rather than soft links?
The proper solution would be to make your own Docker container, but I don’t think HA’s management solution can work with that easily.
Alternatively, you could figure out where HAOS stores the Docker config and add a volume definition of your own. You’ll probably be able to put all of your files in /usr/local/bin by adding a line like “- /path/home/host:/usr/local/bin” in the right place. I don’t know where this config is stored, though.
So it can be done, it just–required a lot of steps and me making a mapping spreadsheet of all the containers. But! Automations and scripts run in the homeassistant container, while when you ssh, you’re going into the ssh addon container which should have been obvious and really was once I finished mapping all the containers.
Goal: I need /usr/local/bin in the ssh container so I can run scripts over ssh and access my function library script easily without ./path/to/script.
Summary: ssh into HAOS from the homeassistant container with an HAOS root user (port 22222), run docker exec to get into the ssh addon container, then make your symlinks for /usr/local/bin.
(Note: this is ridiculously complicated and I know there has to be a better way. But this works so I win.)
- Get access to HAOS itself as root: https://developers.home-assistant.io/docs/operating-system/debugging. Verify you can login successfully.
- In homeassistant container:
- a. create an .ssh folder (/config/.ssh)
- b. add the authorized_keys file you made for step one.
- c. add the public and private keys you made for step one (should be in the ssh addon container).
- d. set permissions;
chmod 600 /config/.ssh/authorized_keys chmod 600 /config/.ssh/PRIVATE_KEY chmod 644 /config/.ssh/PUBLIC_KEY chmod 700 /config/.ssh
- e. In /config/shell_scripts.yaml or wherever you put your shell scripts, add the script you want to use to update /usr/local/bin: UPDATE_BIN_SCRIPT: /config/shell_scripts/UPDATE_BIN_SCRIPT
- f. Restart HA.
- g. Check it in Developer Tools->Services
I have no idea how consistent the ssh addon container name is usually but it’s different on all three of my installs, so insert your container name for SSH_ADDON_CONTAINER_NAME
Steps: login to HAOS, go into the SSH Container, and do the update. This is horribly messy but hey, it works.
UPDATE_BIN_SCRIPT
#!/bin/bash # OPTIONAL: Update some of the very outdated alpine packages in both homeassistant and the ssh addon (figlet makes cool ascii art of my server # name). You'll need to run it twice; once for the homeassistant container, then again in the ssh container. Assuming you want to update packages, # anyway # update homeassistant container packages apk add coreutils figlet iproute2 iw jq ncurses procps-ng sed util-linux wireless-tools # ssh into HAOS and access docker container ssh -i /config/.ssh/PRIVATE_KEY -p 22222 root@HA_IP_ADDRESS << EOF docker exec SSH_ADDON_CONTAINER_NAME \ bash -c \ 'apk add coreutils figlet iproute2 iw jq ncurses procps-ng sed util-linux wireless-tools; \ if [ ! -h /usr/local/bin/SCRIPT1 ]; then echo "SCRIPT1 does not exist"; \ ln -s /homeassistant/shell_scripts/SCRIPT1 /usr/local/bin/SCRIPT1; echo "Link created"; \ else echo "Link exists";fi; \ if [ ! -h /usr/local/bin/SCRIPT2 ]; then echo "SCRIPT2 does not exist"; \ ln -s /homeassistant/shell_scripts/SCRIPT2 /usr/local/bin/SCRIPT2; echo "Link created"; \ else echo "Link exists";fi' EOF echo "Done"
I am going to feel really stupid when I find out there’s a much easier way.
Going in through SSH should be pretty reliable.
I would’ve looked into something like HACS to write a custom integration (even if that integration is just a Python script calling bash), but then again that has tons of moving parts and probably requires publishing your stuff online somewhere.
Or, I suppose, you could put your shell script somewhere in the volume where config is stored, and use the shell integration to launch it. I don’t think that’ll have the permissions you need, though.
The shell integration is why this happened.; I wanted to run the update script as a service so it could be triggered when the Supervisor or Core versions changed so it would automatically symlink my scripts in /usr/local/bin in the ssh_addon container. The shell integration runs in the homeassistant container, so that’s when it became complicated.
Docker containers are designed to be immutable. The moment they’re stopped and recreated, any changes to them ads thrown out. You’re supposed to add a layer to your Docker image if you want to add command lines and such. That’s why it’ll keep deleting your stuff every time you update.
It took me until I put Home Assistant on my server in a docker container to realize what was going on there. I use docker more now, but it’s really, really nothing like this.
Running the script inside Docker should put it in the right place, but I wouldn’t advice doing it that way.
That’s what I’ve been doing manually over regular ssh (not the 22222 port one).
To work around the path issue, maybe consider using hard links rather than soft links?
That’s what I think I need to do, but the only ‘hard’ links–at least according to multiple find -name/find -iname searches on the ssh 22222 port–are all in /mnt/data/docker/overlay2 and /var/lib/docker/overlay2. I get there’s a working pattern with the overlays but dear God why.
Alternatively, you could figure out where HAOS stores the Docker config and add a volume definition of your own. You’ll probably be able to put all of your files in /usr/local/bin by adding a line like “- /path/home/host:/usr/local/bin” in the right place. I don’t know where this config is stored, though.
Okay that makes sense. I guess the first step is to get the container structure and volume.
Thanks so much! I’ll update if I find the solution or die trying.