Skip to content

Commit 1938a7f

Browse files
authored
Merge pull request #59 from macmpi/dev
Version 1.7
2 parents 4ac1206 + 06d9bc8 commit 1938a7f

14 files changed

Lines changed: 434 additions & 154 deletions

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2022-2025, macmpi
3+
Copyright (c) 2022-2026, macmpi
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,22 @@ As with Alpine Linux initial bring-up, `root` account has no password initially.
1919
From there, actual system install can be performed as usual with `setup-alpine` for instance (check Alpine [wiki](https://wiki.alpinelinux.org/wiki/Alpine_setup_scripts#setup-alpine) for details).
2020

2121
## Extra configuration:
22-
Extra files may be added next to `headless.apkovl.tar.gz` to customise boostrapping configuration (check `sample_*` files):
22+
Extra files may be added next to `headless.apkovl.tar.gz` to customise boostrapping configuration (check [contribs](https://github.com/macmpi/alpine-linux-headless-bootstrap/tree/main/contribs) sample files):
2323
- `wpa_supplicant.conf`[^3] (*mandatory for wifi*): define wifi SSID, password and regulatory country [code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).
24-
- `unattended.sh`[^3] (*optional*): provide a deployment script to automate setup & customizations during initial bootstrap *(check users' contributed [samples](https://github.com/macmpi/alpine-linux-headless-bootstrap/discussions/categories/unattended-sh-samples) and share yours)*.
24+
- `unattended.sh`[^3] (*optional*): provide a deployment script to automate setup & customizations during initial bootstrap *(see [contribs](https://github.com/macmpi/alpine-linux-headless-bootstrap/tree/main/contribs) or users' contributed [samples](https://github.com/macmpi/alpine-linux-headless-bootstrap/discussions/categories/unattended-sh-samples) and share yours)*.
2525
- `interfaces`[^3] (*optional*): define network interfaces at will, if defaults DCHP-based are not suitable.
2626
- `authorized_keys` (*optional*): provide client's public SSH key to secure `root` ssh login.
2727
- `ssh_host_*_key*` (*optional*): provide server's custom ssh keys to be injected (may be stored), instead of using temporarily bundled ones[^4] (not stored). Providing an empty key file will trigger new keys generation (ssh server may take longer to start).
2828
- `opt-out` (*optional*): dummy file to opt-out internet features (connection status, version check, auto-update) and related links usage anonymous [telemetry](https://is.gd/privacy.php).
2929
- `auto-updt` (*optional*): enable automatic `headless.apkovl.tar.gz` file update with latest from master branch. If it contains `reboot` keyword all in one line, system will reboot after succesful update (unless ssh session is active or `unattended.sh` script is available).
3030

31-
Main execution steps are logged: `cat /var/log/messages | grep headless`.
31+
Main execution steps are logged: `cat /var/log/messages | grep headless`.\
32+
For more details: `cat /tmp/alhb`.
3233

3334
## Seamless USB-gadget mode:
34-
Devices with UDC controller (*e.g., PiZero*) can expose the following features over USB port: serial console, ethernet interface and mass-storage
35+
Devices with UDC controller (*e.g., PiZero*) can expose the following features over USB port: serial console, ethernet interface and mass-storage.
3536

36-
To enable them, make sure `dwc2` (or `dwc3`) driver is **previously loaded** on capable device, **and** configuration is set to **OTG peripheral** mode: depending on devices, this may be driven by hardware (including cable) and/or software.\
37+
To enable them, make sure `dwc2` (or `dwc3`) driver is **previously loaded** on device, **and** configuration is set to **OTG peripheral** mode: depending on devices, this may be driven by hardware (including cable) and/or software.\
3738
(e.g., on supporting Pi devices[^5], just add `dtoverlay=dwc2,dr_mode=peripheral` in `usercfg.txt` to force both by software)
3839

3940
Plug USB cable into host Computer port before booting device.

sample_auto-updt renamed to contribs/auto-updt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: Copyright 2022-2025, macmpi
1+
# SPDX-FileCopyrightText: Copyright 2022-2026, macmpi
22
# SPDX-License-Identifier: MIT
33

44
# Automated reboot after update is cancelled if:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: Copyright 2022-2025, macmpi
1+
# SPDX-FileCopyrightText: Copyright 2022-2026, macmpi
22
# SPDX-License-Identifier: MIT
33

44
# Sample network interfaces file
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/sh
22

3-
# SPDX-FileCopyrightText: Copyright 2022-2025, macmpi
3+
# SPDX-FileCopyrightText: Copyright 2022-2026, macmpi
44
# SPDX-License-Identifier: MIT
55

66
## collection of few code snippets as sample unnatteded actions some may find usefull
@@ -11,19 +11,19 @@
1111
# only keep a single starting # on the line below
1212
##NO_SSH
1313

14-
# Uncomment to enable stdout and errors redirection to console (service won't show messages)
15-
# exec 1>/dev/console 2>&1
14+
# Uncomment to redirect stdout and errors to logfile as service won't show messages
15+
# exec 1>>/tmp/alhb 2>&1
1616

1717
# shellcheck disable=SC2142 # known special case
1818
alias _logger='logger -st "${0##*/}"'
1919

20+
2021
## Obvious one; reminder: is run as background service
2122
_logger "hello world !!"
2223
sleep 60
2324
_logger "Finished script"
24-
########################################################
25-
2625

26+
########################################################
2727
## This snippet removes apkovl file on volume after initial boot
2828
# grab used ovl filename from dmesg
2929
ovl="$( dmesg | grep -o 'Loading user settings from .*:' | awk '{print $5}' | sed 's/:.*$//' )"
@@ -44,8 +44,6 @@ rm -f "${ovl}"
4444
_is_ro && mount -o remount,ro "${ovlpath}"
4545

4646
########################################################
47-
48-
4947
## This snippet configures Minimal diskless environment
5048
# note: with INTERFACESOPTS=none, no networking will be setup so it won't work after reboot!
5149
# Change it or run setup-interfaces in interractive mode afterwards (and lbu commit -d thenafter)
@@ -68,7 +66,7 @@ cat <<-EOF > /tmp/ANSWERFILE
6866
INTERFACESOPTS=none
6967
7068
# Set Public nameserver
71-
DNSOPTS="-n 9.9.9.9"
69+
DNSOPTS="-n 1.1.1.1"
7270
7371
# Set timezone to UTC
7472
TIMEZONEOPTS="UTC"
@@ -77,7 +75,7 @@ cat <<-EOF > /tmp/ANSWERFILE
7775
PROXYOPTS=none
7876
7977
# Add first mirror (CDN)
80-
APKREPOSOPTS="-1"
78+
APKREPOSOPTS="-1 -c"
8179
8280
# Do not create any user
8381
USEROPTS=none
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
#!/bin/sh
2+
3+
# SPDX-FileCopyrightText: Copyright 2025-2026, macmpi
4+
# SPDX-License-Identifier: MIT
5+
6+
## Install minimal sys-based Alpine with Home-assistant docker image (with tailscale and mosquitto)
7+
## e.g., on 512MB PiZeroW (armhf), uses ~290MB RAM while running; leaves ~170MB RAM available
8+
## PiZero2W should stick to 32bit (armv7) releases, as 512MB RAM is too tight for 64bit containers
9+
## Home-Assistant 32bit last release is 2025.11.3
10+
11+
# HOW TO USE (Customize MY_xxxx values to your needs. Defaults are ok for Pi)
12+
# - prepare install media (Alpine 3.23 and later) as per Alpine wiki for your target hardware
13+
# - add headless.apkovl.tar.gz, this file (as unattended.sh) and wpa_supplicant.conf (if wifi) onto media
14+
# - boot machine, and let unattended install proceed & reboot (may be observed via root ssh login)
15+
# - after reboot, log-in as admin user via ssh (WARNING change default password in MY_PASS)
16+
# - execute: doas ./update_container.sh
17+
# - be patient as initial home-assistant image pull may take (very) long time, ~1h15 on PiZero
18+
# - then setup home-assistant from remote machine via Web interface (avoid logs to preserve SD)
19+
# - associate tailscale to your account info if needed for remote access (enabled by default)
20+
# - finetune mosquitto if needed (enabled by default, WARNING unsecured anonymous allowed on port 1883)
21+
22+
## CUSTOMIZE values below to your needs
23+
MY_USER="alpine" # admin account user name
24+
MY_PASS="enipla" # password for that user
25+
MY_IFACE="wlan0" # network interface to be used; may be eth0, etc...(DHCP by default)
26+
MY_DISK="mmcblk0" # WARNING: this disk dev will be erased for good -- double-check!!
27+
MY_BOOT="${MY_DISK}p1" # dev partition for bootfs on related disk, usually 1st partition
28+
MY_ROOT="${MY_DISK}p2" # dev partition for rootfs related disk, may be 3rd if swap is present
29+
MY_ROOT_SIZE="$((6*2*1024))" # rootfs partition size in MB (allow twice the minimum size for containers updates)
30+
# set to false if willing to use none, or sibling containers (check availability for arch)
31+
NATIVE_TAILSCALE=true
32+
NATIVE_MQTT=true
33+
34+
# Uncomment to redirect stdout and errors to logfile as service won't show messages
35+
# exec 1>>/tmp/alhb 2>&1
36+
37+
# shellcheck disable=SC2142 # known special case
38+
alias _logger='logger -st "${0##*/}"'
39+
40+
# Last Home Assistant docker image for 32-bit (armhf,armv7,x86) is tagged '2025.11.3' not 'stable'
41+
# https://www.home-assistant.io/blog/2025/06/11/release-20256/#deprecating-installation-methods-and-32-bit-architectures
42+
case "$(cat /etc/apk/arch)" in
43+
armhf|armv7|x86) TAG="2025.11.3";;
44+
aarch64|x86_64) TAG="stable";;
45+
*) _logger "Unavailable container image! Exiting..."; exit 1;;
46+
esac
47+
48+
# grab used ovl filename from dmesg
49+
ovl="$( dmesg | grep -o 'Loading user settings from .*:' | awk '{print $5}' | sed 's/:.*$//' )"
50+
if [ -f "${ovl}" ]; then
51+
ovlpath="$( dirname "$ovl" )"
52+
else
53+
# search path again as mountpoint have been changed later in the boot process...
54+
ovl="$( basename "${ovl}" )"
55+
ovlpath=$( find /media -maxdepth 2 -type d -path '*/.*' -prune -o -type f -name "${ovl}" -exec dirname {} \; | head -1 )
56+
ovl="${ovlpath}/${ovl}"
57+
fi
58+
59+
# Setup wifi if available
60+
if [ -e "$ovlpath/wpa_supplicant.conf" ]; then
61+
apk add wpa_supplicant
62+
cp "$ovlpath/wpa_supplicant.conf" /etc/wpa_supplicant/wpa_supplicant.conf
63+
rc-update add wpa_supplicant boot
64+
_logger "Wifi configured"
65+
fi
66+
67+
_logger "Starting base sys disk installation"
68+
cat <<-EOF > /tmp/ANSWERFILE
69+
KEYMAPOPTS=none
70+
HOSTNAMEOPTS=home-assistant
71+
DEVDOPTS=mdev
72+
INTERFACESOPTS="auto lo
73+
iface lo inet loopback
74+
75+
auto $MY_IFACE
76+
iface $MY_IFACE inet dhcp
77+
"
78+
DNSOPTS=""
79+
TIMEZONEOPTS=UTC
80+
PROXYOPTS=none
81+
APKREPOSOPTS="-1 -c"
82+
USEROPTS="-a -u $MY_USER"
83+
SSHDOPTS=openssh
84+
NTPOPTS=chrony
85+
86+
export ERASE_DISKS=/dev/$MY_DISK
87+
export ROOT_SIZE=$MY_ROOT_SIZE
88+
DISKOPTS="-m sys /dev/$MY_DISK"
89+
EOF
90+
91+
SSH_CONNECTION="FAKE" setup-alpine -ef /tmp/ANSWERFILE
92+
93+
# Prep install script for destination sys-based system
94+
_logger "Prepare sys-setup script"
95+
cat <<-EOF1 >/tmp/setup_homeassistant.sh
96+
#!/bin/sh
97+
98+
echo "$MY_USER:$MY_PASS" | chpasswd
99+
passwd -l root
100+
101+
apk update
102+
apk upgrade --available
103+
104+
# On Pi, reclaim ~48MB more RAM for CPU (GPU minimal)
105+
if grep -q "Raspberry Pi" /proc/device-tree/model 2>/dev/null; then
106+
apk add raspberrypi-bootloader-cutdown
107+
echo "gpu_mem=16" >> /boot/config.txt
108+
fi
109+
110+
apk add bluez
111+
rc-update add bluetooth default
112+
113+
apk add docker docker-compose
114+
# TBD rootless config: https://wiki.alpinelinux.org/wiki/Docker#Docker_rootless
115+
# adduser -G docker $MY_USER
116+
# apk add docker-rootless-extras shadow-subids
117+
# echo "# Sets config for Docker rootless >> /etc/rc.conf
118+
# echo "rc_cgroup_mode=\"unified\"" >> /etc/rc.conf
119+
# rc-update add cgroups default
120+
# echo "$MY_USER:231072:65536" >> /etc/subuid
121+
# echo "$MY_USER:231072:65536" >> /etc/subgid
122+
123+
rc-update add docker default
124+
125+
# admin account has home-assistant docker-related setup and config files
126+
mkdir -p /home/$MY_USER/homeassistant # home-assistant config directory
127+
chown $MY_USER /home/$MY_USER/homeassistant
128+
cat <<-EOF2 >/home/$MY_USER/compose.yaml
129+
services:
130+
homeassistant:
131+
image: ghcr.io/home-assistant/home-assistant:$TAG
132+
container_name: homeassistant
133+
privileged: true
134+
restart: unless-stopped
135+
network_mode: host
136+
environment:
137+
- TZ=Europe/Paris
138+
- PUID=$(id -u $MY_USER)
139+
- PGID=$(id -u $MY_USER)
140+
- UMASK=007
141+
volumes:
142+
- /run/dbus:/run/dbus:ro
143+
- /home/$MY_USER/homeassistant:/config
144+
EOF2
145+
chown $MY_USER /home/$MY_USER/compose.yaml
146+
147+
cat <<-EOF2 >/home/$MY_USER/update_container.sh
148+
#!/bin/sh
149+
150+
! [ "$(id -u)" -eq 0 ] && { echo "Please run with administrator privileges." >&2; exit 1; }
151+
152+
echo "This may be (very) long...grab a coffee (or more)!"
153+
docker-compose pull && \
154+
docker-compose up -d && \
155+
docker image prune -af
156+
EOF2
157+
chmod +x /home/$MY_USER/update_container.sh
158+
chown $MY_USER /home/$MY_USER/update_container.sh
159+
160+
# Optional native add-on components (set install option accordingly)
161+
if [ "$NATIVE_TAILSCALE" = "true" ]; then
162+
apk add tailscale
163+
# do not run as root
164+
sed -i 's/^#command_user=.*/command_user=\"tailscale:tailscale\"/' /etc/conf.d/tailscale
165+
rc-update add tailscale default
166+
fi
167+
if [ "$NATIVE_MQTT" = "true" ]; then
168+
apk add mosquitto
169+
# WARNING unsecured anonymous: add password file & disable anonymous
170+
cat <<-EOF2 >>/etc/mosquitto/mosquitto.conf
171+
allow_anonymous true
172+
listener 1883
173+
EOF2
174+
rc-update add mosquitto default
175+
fi
176+
177+
EOF1
178+
chmod +x /tmp/setup_homeassistant.sh
179+
180+
_logger "Mounting new system for post-installation"
181+
mkdir -p /mnt/boot /mnt/tmp /mnt/dev /mnt/proc /mnt/sys
182+
mount /dev/$MY_ROOT /mnt
183+
mount /dev/$MY_BOOT /mnt/boot
184+
mount --bind /tmp /mnt/tmp
185+
mount --bind /dev /mnt/dev
186+
mount --bind /proc /mnt/proc
187+
mount --bind /sys /mnt/sys
188+
189+
_logger "Running sys-setup script on disk-based system"
190+
chroot /mnt /tmp/setup_homeassistant.sh
191+
sync
192+
193+
_logger "Cleaning up mounts"
194+
umount /mnt/sys
195+
umount /mnt/proc
196+
umount /mnt/dev
197+
umount /mnt/tmp
198+
umount /mnt/boot
199+
umount /mnt
200+
201+
_logger "Finished unattended script - rebooting system"
202+
reboot
203+

0 commit comments

Comments
 (0)