Compare commits

..
3 Commits
Author SHA1 Message Date
Damien 294960f44d gitea-runner: detect existing LXC on re-run, refresh OS on update
Ports openbao's host-side update path to gitea-runner: main() now uses
find_existing_lxc (hostname/tag match) to switch into a new update_lxc()
instead of always recreating the container, with an explicit --update
dispatch case reaching update_runner() (previously inferred only from
/usr/local/bin/act_runner presence). require_root is ported from openbao
for the host-side pct branch.

Extracted a small exec_in_lxc() helper (option a from the two offered)
rather than duplicating the curl-pipe invocation, since main() now needs
to drive both the create and update paths through the same piping logic
with only the trailing --install/--update flag differing — matching
openbao's own exec_in_lxc for consistency.

update_runner() now calls refresh_os_packages before touching the binary,
mirroring openbao's update_inside_lxc(). Also switched create_lxc's LXC
tag from the hardcoded "cicd" to the LXC_TAG variable (added in the prior
commit but unused until now) so find_existing_lxc's tag match actually
works.

Documented the OS-refresh-on-update behavior in both READMEs.

Closes #15
2026-07-30 11:28:14 +02:00
Damien 51f2474db6 openbao,gitea-runner: source lib/common.sh, fix hardcoded Alpine template
Both install scripts now source lib/common.sh instead of duplicating
detect_latest_alpine_template(), find_existing_lxc(), and the tty1
autologin block. Since these scripts are distributed via curl one-liner
and piped into `pct exec` inside the LXC, there is no local checkout to
source from in those contexts — lib/common.sh is sourced from disk when
a local checkout is available (BASH_SOURCE resolves to a real path),
otherwise fetched over HTTP next to SCRIPT_URL. Both scripts already
require outbound network to curl themselves and to download their
respective binaries, so this adds no new failure mode.

openbao/install.sh is a behavioral no-op: same log output, same control
flow. One inline apk update/upgrade is intentionally left as-is in
update_lxc() — refresh_os_packages() is a bash function in this
process and can't run over `pct exec ... sh -c` without shipping the
function definition into the container.

gitea-runner/install.sh fixes the actual bug: TEMPLATE was hardcoded to
a specific dated Alpine release, so create_lxc() would keep trying to
provision a stale/absent template. TEMPLATE now defaults to empty and
create_lxc() calls detect_latest_alpine_template() when unset, mirroring
openbao. Also renamed HOSTNAME -> HOSTNAME_LXC and added LXC_TAG to
match openbao's naming, since a follow-up (issue #15) will wire up
find_existing_lxc()-based update detection here.

Closes #12, Closes #14
2026-07-30 11:23:03 +02:00
Damien d0138b742d lib/common.sh: template detection + autologin + update helpers 2026-07-30 11:14:20 +02:00
5 changed files with 179 additions and 89 deletions
+3 -2
View File
@@ -20,7 +20,7 @@ Single script, three automatic modes:
bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh)" bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh)"
``` ```
The script automatically creates an Alpine 3.23 LXC with Docker and act_runner. The script automatically creates an Alpine LXC (template auto-detected from `pveam available`) with Docker and act_runner.
#### Customization #### Customization
@@ -34,6 +34,7 @@ CTID=120 HOSTNAME=runner-02 CORES=4 RAM=4096 bash -c "$(curl -fsSL https://gitea
|----------|---------|-------------| |----------|---------|-------------|
| `CTID` | auto | Container ID | | `CTID` | auto | Container ID |
| `RUNNER_HOSTNAME` | `gitea-runner` | LXC Hostname | | `RUNNER_HOSTNAME` | `gitea-runner` | LXC Hostname |
| `TEMPLATE` | auto-detected | Alpine template; auto-detected from `pveam available` |
| `CORES` | `2` | CPU cores | | `CORES` | `2` | CPU cores |
| `RAM` | `2048` | RAM in MiB | | `RAM` | `2048` | RAM in MiB |
| `DISK` | `8` | Disk in GB | | `DISK` | `8` | Disk in GB |
@@ -59,7 +60,7 @@ From inside the LXC:
curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh | bash curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh | bash
``` ```
The script detects that act_runner is already installed and switches to update mode automatically. The script detects that act_runner is already installed and switches to update mode automatically. Re-running from the Proxmox host does the same, plus refreshes the LXC's Alpine packages first (`apk update && apk upgrade`).
### Architecture ### Architecture
+102 -31
View File
@@ -8,14 +8,15 @@ set -euo pipefail
# --- Config (override via environment) --- # --- Config (override via environment) ---
CTID="${CTID:-}" CTID="${CTID:-}"
HOSTNAME="${RUNNER_HOSTNAME:-gitea-runner}" HOSTNAME_LXC="${RUNNER_HOSTNAME:-gitea-runner}"
TEMPLATE="${TEMPLATE:-alpine-3.23-default_20260116_amd64.tar.xz}" TEMPLATE="${TEMPLATE:-}" # auto-detected when empty
STORAGE="${STORAGE:-local-lvm}" STORAGE="${STORAGE:-local-lvm}"
TEMPLATE_STORAGE="${TEMPLATE_STORAGE:-local}" TEMPLATE_STORAGE="${TEMPLATE_STORAGE:-local}"
CORES="${CORES:-2}" CORES="${CORES:-2}"
RAM="${RAM:-2048}" RAM="${RAM:-2048}"
DISK="${DISK:-8}" DISK="${DISK:-8}"
BRIDGE="${BRIDGE:-vmbr0}" BRIDGE="${BRIDGE:-vmbr0}"
LXC_TAG="${LXC_TAG:-gitea-runner}" # stable identifier for the container
SCRIPT_URL="https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh" SCRIPT_URL="https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh"
GITEA_HOSTNAME="${GITEA_HOSTNAME:-gitea.taila5ad8.ts.net}" GITEA_HOSTNAME="${GITEA_HOSTNAME:-gitea.taila5ad8.ts.net}"
GITEA_API="https://gitea.com/api/v1/repos/gitea/act_runner/releases" GITEA_API="https://gitea.com/api/v1/repos/gitea/act_runner/releases"
@@ -31,6 +32,33 @@ log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
require_root() {
if [[ "$(id -u)" -ne 0 ]]; then
log_error "This script must be run as root (current uid: $(id -u))."
log_error "On Proxmox, launch it from the host shell or via the Web UI shell, both of which run as root."
exit 1
fi
}
# ============================================================
# Load shared helpers (lib/common.sh: detect_latest_alpine_template,
# enable_tty1_autologin, find_existing_lxc, refresh_os_packages).
#
# Same reasoning as openbao/install.sh: a local checkout has the file
# on disk right next to us, but the documented curl one-liner (host or
# piped into `pct exec` inside the LXC) has no BASH_SOURCE path worth
# trusting, so fall back to fetching lib/common.sh over HTTP next to
# SCRIPT_URL. The LXC already needs outbound network to curl this very
# script and to download the act_runner binary, so this adds no new
# failure mode.
# ============================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" 2>/dev/null && pwd || true)"
if [[ -n "$SCRIPT_DIR" && -f "${SCRIPT_DIR}/../lib/common.sh" ]]; then
source "${SCRIPT_DIR}/../lib/common.sh"
else
source <(curl -fsSL "$(dirname "$(dirname "$SCRIPT_URL")")/lib/common.sh")
fi
# --- Helpers --- # --- Helpers ---
get_latest_release() { get_latest_release() {
local release local release
@@ -67,12 +95,34 @@ download_runner() {
echo "$release" > "$VERSION_FILE" echo "$release" > "$VERSION_FILE"
} }
# Inject the script into the container and execute it in the requested mode.
# Forwards the runtime configuration the inner invocation needs to reproduce
# what the user requested on the host (mirrors openbao/install.sh's helper
# of the same name).
exec_in_lxc() {
local ctid="$1"
local mode="$2" # --install or --update
pct exec "$ctid" -- sh -c "apk add --no-cache bash curl jq > /dev/null 2>&1"
curl -fsSL "$SCRIPT_URL" \
| pct exec "$ctid" -- env \
SCRIPT_URL="$SCRIPT_URL" \
GITEA_HOSTNAME="$GITEA_HOSTNAME" \
bash -s -- "$mode"
}
# ============================================================ # ============================================================
# MODE 1: Proxmox host — create LXC container # MODE 1: Proxmox host — create LXC container
# ============================================================ # ============================================================
create_lxc() { create_lxc() {
log_info "=== Gitea Act Runner — LXC Creation ===" log_info "=== Gitea Act Runner — LXC Creation ==="
if [[ -z "$TEMPLATE" ]]; then
TEMPLATE=$(detect_latest_alpine_template)
else
log_info "Using user-provided template: $TEMPLATE"
fi
# Auto-select next CTID if not specified # Auto-select next CTID if not specified
if [[ -z "$CTID" ]]; then if [[ -z "$CTID" ]]; then
CTID=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null \ CTID=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null \
@@ -86,16 +136,16 @@ create_lxc() {
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE"
fi fi
log_info "Creating LXC $CTID ($HOSTNAME)..." log_info "Creating LXC $CTID ($HOSTNAME_LXC)..."
pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" \ pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" \
--hostname "$HOSTNAME" \ --hostname "$HOSTNAME_LXC" \
--cores "$CORES" \ --cores "$CORES" \
--memory "$RAM" \ --memory "$RAM" \
--rootfs "${STORAGE}:${DISK}" \ --rootfs "${STORAGE}:${DISK}" \
--net0 "name=eth0,bridge=${BRIDGE},ip=dhcp" \ --net0 "name=eth0,bridge=${BRIDGE},ip=dhcp" \
--unprivileged 1 \ --unprivileged 1 \
--features nesting=1,keyctl=1 \ --features nesting=1,keyctl=1 \
--tags "infra-script,cicd" \ --tags "infra-script,${LXC_TAG}" \
--start 0 --start 0
log_info "Configuring LXC for Docker and Tailscale..." log_info "Configuring LXC for Docker and Tailscale..."
@@ -111,8 +161,7 @@ EOF
sleep 5 sleep 5
log_info "Injecting install script into container..." log_info "Injecting install script into container..."
pct exec "$CTID" -- sh -c "apk add --no-cache bash curl jq > /dev/null 2>&1" exec_in_lxc "$CTID" "--install"
curl -fsSL "$SCRIPT_URL" | pct exec "$CTID" -- bash -s -- --install
local ip local ip
ip=$(pct exec "$CTID" -- ip -4 addr show eth0 2>/dev/null | awk '/inet /{print $2}' | cut -d/ -f1) ip=$(pct exec "$CTID" -- ip -4 addr show eth0 2>/dev/null | awk '/inet /{print $2}' | cut -d/ -f1)
@@ -122,7 +171,7 @@ EOF
log_info "LXC $CTID created successfully!" log_info "LXC $CTID created successfully!"
log_info "=========================================" log_info "========================================="
echo "" echo ""
echo " Hostname : $HOSTNAME" echo " Hostname : $HOSTNAME_LXC"
echo " IP : ${ip:-pending}" echo " IP : ${ip:-pending}"
echo "" echo ""
echo "Next steps:" echo "Next steps:"
@@ -133,6 +182,28 @@ EOF
echo "" echo ""
} }
# ============================================================
# MODE 1b: Proxmox host — update an existing LXC
# ============================================================
update_lxc() {
local ctid="$1"
log_info "=== Gitea Act Runner — updating existing LXC ${ctid} ==="
if ! pct status "$ctid" | grep -q running; then
log_info "Starting LXC ${ctid}..."
pct start "$ctid"
sleep 3
fi
log_info "Refreshing Alpine packages inside LXC ${ctid}..."
pct exec "$ctid" -- sh -c "apk update >/dev/null && apk upgrade >/dev/null"
log_info "Upgrading act_runner binary inside LXC ${ctid}..."
exec_in_lxc "$ctid" "--update"
log_info "Update of LXC ${ctid} complete."
}
# ============================================================ # ============================================================
# MODE 2: Inside LXC — fresh install # MODE 2: Inside LXC — fresh install
# ============================================================ # ============================================================
@@ -232,24 +303,7 @@ LOGROTATE
ln -sf /usr/sbin/logrotate /etc/periodic/daily/logrotate 2>/dev/null || true ln -sf /usr/sbin/logrotate /etc/periodic/daily/logrotate 2>/dev/null || true
log_info "Enabling console auto-login on tty1..." enable_tty1_autologin
# Alpine ships busybox getty by default; agetty (from util-linux) is what
# supports --autologin.
apk add --no-cache agetty >/dev/null 2>&1 || apk add --no-cache util-linux >/dev/null
# Replace any existing tty1 entry, then append our autologin line. Doing it
# in two steps (delete + append) is more robust than an in-place sed against
# a pattern that may drift across Alpine releases.
sed -i '/^tty1::/d' /etc/inittab
echo 'tty1::respawn:/sbin/agetty --autologin root --noclear 38400 tty1' >> /etc/inittab
# Tell PID 1 to re-read /etc/inittab so the change takes effect without a reboot.
kill -HUP 1 2>/dev/null || true
# Kick any getty/agetty still attached to tty1 so init respawns it *now* with
# the new line — otherwise the first web-console session lands on the stale
# process and the operator has to type `exit` once before autologin kicks in.
pkill -KILL -f '(getty|agetty).*tty1' 2>/dev/null || true
log_info "Cleaning up..." log_info "Cleaning up..."
rm -rf /var/cache/apk/* rm -rf /var/cache/apk/*
@@ -275,6 +329,8 @@ LOGROTATE
update_runner() { update_runner() {
log_info "=== Gitea Act Runner — Update ===" log_info "=== Gitea Act Runner — Update ==="
refresh_os_packages
local release local release
release=$(get_latest_release) release=$(get_latest_release)
@@ -305,12 +361,27 @@ update_runner() {
# Main — detect context # Main — detect context
# ============================================================ # ============================================================
main() { main() {
if [[ "${1:-}" == "--install" ]]; then case "${1:-}" in
# Explicitly called in install mode (from pct exec) --install)
install_runner install_runner
elif command -v pct &> /dev/null; then return
;;
--update)
update_runner
return
;;
esac
if command -v pct &> /dev/null; then
# We're on the Proxmox host # We're on the Proxmox host
create_lxc require_root
local existing=""
if existing=$(find_existing_lxc); then
log_info "Found existing gitea-runner LXC (CTID ${existing}, hostname/tag match) — switching to update mode."
update_lxc "$existing"
else
create_lxc
fi
elif [[ -f /usr/local/bin/act_runner ]]; then elif [[ -f /usr/local/bin/act_runner ]]; then
# act_runner exists — update mode # act_runner exists — update mode
update_runner update_runner
+43 -4
View File
@@ -26,7 +26,18 @@ set -euo pipefail
# template if `pveam` is unavailable or returns nothing. # template if `pveam` is unavailable or returns nothing.
# ============================================================ # ============================================================
detect_latest_alpine_template() { detect_latest_alpine_template() {
: local tmpl
tmpl=$(pveam available --section system 2>/dev/null \
| awk '/^system[[:space:]]+alpine-/ {print $2}' \
| sort -V \
| tail -n1)
if [[ -z "$tmpl" ]]; then
log_warn "Could not query pveam; falling back to a known-good Alpine template."
tmpl="alpine-3.22-default_20250617_amd64.tar.xz"
fi
log_info "Selected Alpine template: $tmpl"
echo "$tmpl"
} }
# ============================================================ # ============================================================
@@ -34,7 +45,24 @@ detect_latest_alpine_template() {
# Idempotent: safe to call on every install/update. # Idempotent: safe to call on every install/update.
# ============================================================ # ============================================================
enable_tty1_autologin() { enable_tty1_autologin() {
: log_info "Enabling console auto-login on tty1..."
# Alpine ships busybox getty by default; agetty (from util-linux) is what
# supports --autologin.
apk add --no-cache agetty >/dev/null 2>&1 || apk add --no-cache util-linux >/dev/null
# Replace any existing tty1 entry, then append our autologin line. Doing it
# in two steps (delete + append) is more robust than an in-place sed against
# a pattern that may drift across Alpine releases.
sed -i '/^tty1::/d' /etc/inittab
echo 'tty1::respawn:/sbin/agetty --autologin root --noclear 38400 tty1' >> /etc/inittab
# Tell PID 1 to re-read /etc/inittab so the change takes effect without a reboot.
kill -HUP 1 2>/dev/null || true
# Kick any getty/agetty still attached to tty1 so init respawns it *now* with
# the new line — otherwise the first web-console session lands on the stale
# process and the operator has to type `exit` once before autologin kicks in.
pkill -KILL -f '(getty|agetty).*tty1' 2>/dev/null || true
} }
# ============================================================ # ============================================================
@@ -45,7 +73,17 @@ enable_tty1_autologin() {
# find_existing_lxc already does). # find_existing_lxc already does).
# ============================================================ # ============================================================
find_existing_lxc() { find_existing_lxc() {
: local id host tags
while read -r id _; do
[[ -z "$id" || "$id" == "VMID" ]] && continue
host=$(pct config "$id" 2>/dev/null | awk -F': ' '/^hostname:/ {print $2}' || true)
tags=$(pct config "$id" 2>/dev/null | awk -F': ' '/^tags:/ {print $2}' || true)
if [[ "$host" == "$HOSTNAME_LXC" ]] || [[ ",${tags//;/,}," == *",${LXC_TAG},"* ]]; then
echo "$id"
return 0
fi
done < <(pct list | awk 'NR>1 {print $1}')
return 1
} }
# ============================================================ # ============================================================
@@ -53,5 +91,6 @@ find_existing_lxc() {
# Callable both host-side (via pct exec) and inside the LXC. # Callable both host-side (via pct exec) and inside the LXC.
# ============================================================ # ============================================================
refresh_os_packages() { refresh_os_packages() {
: log_info "Refreshing Alpine packages..."
apk update >/dev/null && apk upgrade >/dev/null
} }
+3 -1
View File
@@ -110,7 +110,9 @@ curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/feat/lxc-Open
The script auto-detects the presence of `/usr/local/bin/bao` and switches to The script auto-detects the presence of `/usr/local/bin/bao` and switches to
update mode. The OpenRC service is stopped, the binary is swapped (the old one update mode. The OpenRC service is stopped, the binary is swapped (the old one
is kept as `bao.bak.<ts>`), then the service is restarted. is kept as `bao.bak.<ts>`), then the service is restarted. Re-running from the
Proxmox host does the same, plus refreshes the LXC's Alpine packages first
(`apk update && apk upgrade`).
### Architecture ### Architecture
+28 -51
View File
@@ -56,6 +56,29 @@ log_info() { echo -e "${GREEN}[INFO]${NC} $1" >&2; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" >&2; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" >&2; }
log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; } log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
# ============================================================
# Load shared helpers (lib/common.sh: detect_latest_alpine_template,
# enable_tty1_autologin, find_existing_lxc, refresh_os_packages).
#
# This script runs in three different contexts, only one of which has a
# real file on disk next to it:
# - local checkout (`bash openbao/install.sh`) -> lib/common.sh
# sits right there at ../lib/common.sh, source it straight from disk.
# - Proxmox host, documented one-liner (`bash -c "$(curl ... )"`)
# -> no checkout, no BASH_SOURCE path worth trusting.
# - inside the LXC (exec_in_lxc does `curl ... | pct exec ... bash -s --`)
# -> same story, script arrives on stdin.
# For the latter two we fetch lib/common.sh over HTTP, next to SCRIPT_URL.
# The LXC already needs outbound network to curl this very script and to
# download the bao binary, so this adds no new failure mode.
# ============================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" 2>/dev/null && pwd || true)"
if [[ -n "$SCRIPT_DIR" && -f "${SCRIPT_DIR}/../lib/common.sh" ]]; then
source "${SCRIPT_DIR}/../lib/common.sh"
else
source <(curl -fsSL "$(dirname "$(dirname "$SCRIPT_URL")")/lib/common.sh")
fi
# ============================================================ # ============================================================
# Generic helpers # Generic helpers
# ============================================================ # ============================================================
@@ -204,37 +227,6 @@ configure_tailscale_proxy() {
# Proxmox-host helpers # Proxmox-host helpers
# ============================================================ # ============================================================
# Detect newest Alpine LXC template available from the Proxmox repos.
detect_latest_alpine_template() {
local tmpl
tmpl=$(pveam available --section system 2>/dev/null \
| awk '/^system[[:space:]]+alpine-/ {print $2}' \
| sort -V \
| tail -n1)
if [[ -z "$tmpl" ]]; then
log_warn "Could not query pveam; falling back to a known-good Alpine template."
tmpl="alpine-3.22-default_20250617_amd64.tar.xz"
fi
log_info "Selected Alpine template: $tmpl"
echo "$tmpl"
}
# Find an existing LXC by tag or hostname. Echoes CTID, returns 1 if none.
find_existing_lxc() {
local id host tags
while read -r id _; do
[[ -z "$id" || "$id" == "VMID" ]] && continue
host=$(pct config "$id" 2>/dev/null | awk -F': ' '/^hostname:/ {print $2}' || true)
tags=$(pct config "$id" 2>/dev/null | awk -F': ' '/^tags:/ {print $2}' || true)
if [[ "$host" == "$HOSTNAME_LXC" ]] || [[ ",${tags//;/,}," == *",${LXC_TAG},"* ]]; then
echo "$id"
return 0
fi
done < <(pct list | awk 'NR>1 {print $1}')
return 1
}
ensure_template_present() { ensure_template_present() {
local tmpl="$1" local tmpl="$1"
if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$tmpl"; then if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$tmpl"; then
@@ -363,6 +355,9 @@ update_lxc() {
fi fi
log_info "Refreshing Alpine packages inside LXC ${ctid}..." log_info "Refreshing Alpine packages inside LXC ${ctid}..."
# refresh_os_packages() is a bash function local to this process; it can't
# run over `pct exec ... sh -c` without shipping the function definition
# into the container, so this call site stays inline rather than dedupe.
pct exec "$ctid" -- sh -c "apk update >/dev/null && apk upgrade >/dev/null" pct exec "$ctid" -- sh -c "apk update >/dev/null && apk upgrade >/dev/null"
log_info "Upgrading bao binary inside LXC ${ctid}..." log_info "Upgrading bao binary inside LXC ${ctid}..."
@@ -474,24 +469,7 @@ EOF
log_info "Starting openbao service..." log_info "Starting openbao service..."
rc-service openbao start || log_warn "openbao failed to start — inspect /var/log/openbao.log" rc-service openbao start || log_warn "openbao failed to start — inspect /var/log/openbao.log"
log_info "Enabling console auto-login on tty1..." enable_tty1_autologin
# Alpine ships busybox getty by default; agetty (from util-linux) is what
# supports --autologin.
apk add --no-cache agetty >/dev/null 2>&1 || apk add --no-cache util-linux >/dev/null
# Replace any existing tty1 entry, then append our autologin line. Doing it
# in two steps (delete + append) is more robust than an in-place sed against
# a pattern that may drift across Alpine releases.
sed -i '/^tty1::/d' /etc/inittab
echo 'tty1::respawn:/sbin/agetty --autologin root --noclear 38400 tty1' >> /etc/inittab
# Tell PID 1 to re-read /etc/inittab so the change takes effect without a reboot.
kill -HUP 1 2>/dev/null || true
# Kick any getty/agetty still attached to tty1 so init respawns it *now* with
# the new line — otherwise the first web-console session lands on the stale
# process and the operator has to type `exit` once before autologin kicks in.
pkill -KILL -f '(getty|agetty).*tty1' 2>/dev/null || true
configure_tailscale_proxy configure_tailscale_proxy
@@ -563,8 +541,7 @@ MOTD
# ============================================================ # ============================================================
update_inside_lxc() { update_inside_lxc() {
log_info "=== OpenBao — update ===" log_info "=== OpenBao — update ==="
apk update >/dev/null refresh_os_packages
apk upgrade >/dev/null
install_or_upgrade_bao install_or_upgrade_bao
configure_tailscale_proxy configure_tailscale_proxy
log_info "Update complete." log_info "Update complete."