Skip to content

Instantly share code, notes, and snippets.

@Underknowledge
Created May 2, 2026 13:24
Show Gist options
  • Select an option

  • Save Underknowledge/002cb11cc693f1b59b1cadae000976b6 to your computer and use it in GitHub Desktop.

Select an option

Save Underknowledge/002cb11cc693f1b59b1cadae000976b6 to your computer and use it in GitHub Desktop.
Tailscale installer for X1Plus (Bambu Lab X1 Carbon)
#!/bin/sh
# Tailscale installer for a X1 Carbon running X1Plus
# re-run to update and restart manually
set -e
BIN=/usr/sbin
STATE=/mnt/sdcard/tailscale
CACHE_DIR=/mnt/sdcard/tailscale_cache
CACHE=$CACHE_DIR/tailscale_latest_arm.tgz
URL=https://pkgs.tailscale.com/stable/tailscale_latest_arm.tgz
err() { echo "$*" >&2; exit 1; }
[ "$(id -u)" = 0 ] || err "must be root"
[ -d /mnt/sdcard ] || err "no /mnt/sdcard"
command -v start-stop-daemon >/dev/null || err "no start-stop-daemon applet - youre on the right device?"
mkdir -p "$STATE" "$CACHE_DIR"
# Download unless cache is younger than 24h
if [ ! -f "$CACHE" ] || [ $(( $(date +%s) - $(stat -c %Y "$CACHE") )) -gt 86400 ]; then
echo "downloading $URL"
wget -q -O "$CACHE.tmp" "$URL" || err "download failed"
gunzip -t "$CACHE.tmp" 2>/dev/null || err "downloaded file is not gzip"
mv "$CACHE.tmp" "$CACHE"
fi
TMP=$(mktemp -d /tmp/tailscale.XXXXXX)
trap 'rm -rf "$TMP"' EXIT INT TERM
gunzip -c "$CACHE" | ( cd "$TMP" && tar xf - ) || err "extract failed"
SRC=
for d in "$TMP"/tailscale_*_arm; do
[ -d "$d" ] && SRC=$d && break
done
[ -n "$SRC" ] || err "no extracted dir found"
install -m 755 "$SRC/tailscale" "$BIN/tailscale"
install -m 755 "$SRC/tailscaled" "$BIN/tailscaled"
"$BIN/tailscale" version | head -n1
cat > /etc/init.d/S99tailscale <<'EOF'
#!/bin/sh
# tailscaled service. userspace-networking, state on sdcard.
TAILSCALE=/usr/sbin/tailscale
TAILSCALED=/usr/sbin/tailscaled
STATE_DIR=/mnt/sdcard/tailscale
STATE=$STATE_DIR/tailscaled.state
LOG=$STATE_DIR/tailscaled.log
SOCKET=/var/run/tailscale/tailscaled.sock
PID=/var/run/tailscaled.pid
start() {
printf "Starting tailscaled: "
mkdir -p "$STATE_DIR" "$(dirname "$SOCKET")"
[ -f "$LOG" ] && [ "$(stat -c %s "$LOG" 2>/dev/null || echo 0)" -gt 1048576 ] \
&& mv "$LOG" "$LOG.old"
start-stop-daemon -S -m -b -p "$PID" \
--exec /bin/sh -- -c \
"exec $TAILSCALED --tun=userspace-networking --state=$STATE --socket=$SOCKET --port=0 >>$LOG 2>&1"
[ $? = 0 ] && echo OK || echo FAIL
}
stop() {
printf "Stopping tailscaled: "
start-stop-daemon -K -q -p "$PID"
rm -f "$PID"
echo OK
}
status() {
if [ -f "$PID" ] && kill -0 "$(cat "$PID" 2>/dev/null)" 2>/dev/null; then
echo "tailscaled: running (PID $(cat "$PID"))"
out=$(timeout 2 "$TAILSCALE" status 2>/dev/null)
if [ -n "$out" ]; then
echo "$out" | head -n 5
else
echo "(not authenticated; run: tailscale up)"
fi
else
echo "tailscaled: not running"
return 1
fi
}
logs() {
[ -f "$LOG" ] || { echo "no log at $LOG"; exit 1; }
case "$1" in
-f|--follow|follow) tail -n 50 -f "$LOG" ;;
*) tail -n 50 "$LOG" ;;
esac
}
case "$1" in
start) start ;;
stop) stop ;;
restart|reload) stop; sleep 1; start ;;
status) status ;;
logs) shift; logs "$@" ;;
*) echo "usage: $0 {start|stop|restart|status|logs [-f]}"; exit 1 ;;
esac
EOF
chmod 755 /etc/init.d/S99tailscale
cat <<EOF
Installed. Next:
/etc/init.d/S99tailscale start
tailscale up --hostname=x1c
-> open the URL it prints, sign in at tailscale.com, approve
Service: /etc/init.d/S99tailscale {start|stop|restart|status|logs [-f]}
EOF
@Numbski

Numbski commented Jul 7, 2026

Copy link
Copy Markdown

Oh, nice! I had been checking in on native TailScale on X1Plus. How's this been working out for you?

@Underknowledge

Underknowledge commented Jul 8, 2026

Copy link
Copy Markdown
Author

Decent enough. Tools linke OrcaSlicer cant drive auto discovery over wireguard, but thats complaining on a very low level. I enjoy using https://f-droid.org/packages/com.coboltforge.dontmind.multivnc for quick checks on the go.
Just give it a go. As its x1+, ts living on the sd-card only.

@Numbski

Numbski commented Jul 8, 2026

Copy link
Copy Markdown

As I understand it, multicast-over-wireguard is an ongoing WIP, so it could just start working one day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment