diff --git a/README.md b/README.md index 7da36da..0280c15 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ install the same way, with Docker. | [Flowy](https://github.com/selfhostit/flowy) | A private period and cycle tracker | 8092 | | [Troye](https://github.com/selfhostit/troye) | A private calorie and weight tracker (work in progress) | 8093 | -The port is where the installer puts each app, so they can all run on one machine. +The port is where the installer puts each app, so they can all run on one machine. If +another program already has it, the app gets the next free port, and keeps that one. ## Quick start diff --git a/install.sh b/install.sh index ca013d2..03191d8 100644 --- a/install.sh +++ b/install.sh @@ -7,7 +7,8 @@ # To read it before running it, download it first (curl -fsSLO ), # then run `sh install.sh`. security.md says what it does, step by step. # -# It asks which apps you want, gives each one its own folder and port, and runs +# It asks which apps you want, gives each one its own folder and port (the next +# free one when another program has the usual port), and runs # that app's own installer (or its compose file). Then it can put them all on # your Tailscale network with https:// addresses, which phones need: you sign in # with a link, no keys or settings. Run it again to update: every app keeps its @@ -83,6 +84,28 @@ fi $sudo docker compose version >/dev/null 2>&1 || die "Docker Compose v2 is missing: https://docs.docker.com/compose/install/linux/" +# taken_ports: the TCP ports that something on this machine listens on, and the +# ones Docker containers publish (stopped ones too, so they can start again) +taken_ports() { + { ss -ltn 2>/dev/null || netstat -an 2>/dev/null | awk '/^tcp/ && /LISTEN/'; } | + awk '{print $4}' | sed -n 's/.*[:.]\([0-9][0-9]*\)$/\1/p' + ids=$($sudo docker ps -aq 2>/dev/null) || ids= + # shellcheck disable=SC2086 # one argument per container + [ -z "$ids" ] || $sudo docker inspect --format \ + '{{range .HostConfig.PortBindings}}{{range .}}{{.HostPort}} {{end}}{{end}}' $ids 2>/dev/null | + tr ' ' '\n' +} + +# free_port PORT: PORT if nothing uses it, else the first free port after it +free_port() { + taken=" $(taken_ports | tr '\n' ' ') " + p=$1 + while :; do + case $taken in *" $p "*) p=$((p + 1)) ;; *) break ;; esac + done + echo "$p" +} + # --- Which apps --------------------------------------------------------------- chosen=$* @@ -137,7 +160,8 @@ fi # --- Install ------------------------------------------------------------------ # The plasto-based apps (Flowy, Troye) come with deploy/install.sh, which takes -# the folder as its argument and PORT for a new install. An empty TS_AUTHKEY +# the folder as its argument and PORT for a new install (or the next free port +# when that one is taken; Splitwiz's installer does the same). An empty TS_AUTHKEY # skips their own Tailscale question: the shared node below covers them. install_plasto() { # app port dir fetch "https://raw.githubusercontent.com/selfhostit/$1/main/deploy/install.sh" | @@ -154,8 +178,10 @@ install_should_we() { # port dir fetch https://raw.githubusercontent.com/selfhostit/should-we/main/docker-compose.yml | $sudo tee "$2/docker-compose.yml" >/dev/null if [ ! -f "$2/.env" ]; then + port=$(free_port "$1") + [ "$port" = "$1" ] || echo "Port $1 is taken, so should-we gets port $port." tz=${TZ:-$(cat /etc/timezone 2>/dev/null || readlink /etc/localtime 2>/dev/null | sed 's|.*zoneinfo/||' || true)} - printf 'SHOULD_WE_PORT=%s\nTZ=%s\n' "$1" "${tz:-UTC}" | $sudo tee "$2/.env" >/dev/null + printf 'SHOULD_WE_PORT=%s\nTZ=%s\n' "$port" "${tz:-UTC}" | $sudo tee "$2/.env" >/dev/null fi (cd "$2" && $sudo docker compose pull --quiet && $sudo docker compose up -d --remove-orphans) } diff --git a/linux.md b/linux.md index dded5db..ac5e997 100644 --- a/linux.md +++ b/linux.md @@ -55,8 +55,10 @@ curl -fsSL https://raw.githubusercontent.com/selfhostit/docs/main/install.sh | ## Good to know -- **Ports:** Splitwiz 8090, should-we 8091, Flowy 8092, Troye 8093. To change one, edit - the port in that app's `.env` and run the installer again. +- **Ports:** Splitwiz 8090, should-we 8091, Flowy 8092, Troye 8093. If another program + already has one of these when an app is first installed, that app gets the next free + port instead, and the list at the end shows it. An app keeps its port from then on. To + change one, edit the port in that app's `.env` and run the installer again. - **Restarts:** the apps start again by themselves when the machine reboots. - **SD cards** wear out with a database writing to them. A USB SSD is more reliable, and you should keep [backups](updates-and-backups.md) either way.