#!/bin/sh # Installs Kolo on this server: Docker if missing, /opt/kolo with the compose file, Caddy config, the kolo # command and a .env of fresh secrets; then the first update and the first brand with its owner. # curl -fsSL https://get.trykolo.com | sudo sh # Answers can be given in the environment instead of at the prompts: KOLO_BRAND_NAME, KOLO_STAFF_HOST, # KOLO_PORTAL_HOST (or KOLO_ADDRESS_NAME for free trykolo.app addresses), KOLO_OWNER_NAME, KOLO_OWNER_EMAIL, KOLO_LICENCE, # CADDY_EMAIL, KOLO_VERSION. # Without a licence key the install runs a 14-day trial; the key can be added later in the staff area. set -eu IMAGE="ghcr.io/dussto/kolo" DIR=/opt/kolo [ "$(id -u)" = 0 ] || { echo "run as root: curl -fsSL https://get.trykolo.com | sudo sh"; exit 1; } [ "$(uname -s)" = Linux ] || { echo "Kolo installs on a Linux server"; exit 1; } command -v apt-get >/dev/null || echo "note: tested on Ubuntu 22.04/24.04 and Debian 12" [ -f "$DIR/.env" ] && { echo "Kolo is already installed in $DIR; run: kolo update"; exit 0; } ask() { # ask VAR "prompt" [default] eval "v=\${$1:-}" if [ -z "$v" ]; then if [ -t 0 ]; then printf '%s%s: ' "$2" "${3:+ [$3]}"; read -r v /dev/null; then openssl rand -base64 32; else head -c 32 /dev/urandom | base64; fi; } # The hub fills its own address in when it serves this script; a copy from the repository talks to production. HUB_URL="https://account.trykolo.com"; case "$HUB_URL" in __*) HUB_URL="https://account.trykolo.com" ;; esac echo "Kolo installer" ask KOLO_BRAND_NAME "Your hosting brand's name" # Two free addresses on trykolo.app, pointed at this server by Kolo, until the host moves to its own domain in # Settings > Addresses. Typing "own" skips them; KOLO_STAFF_HOST and KOLO_PORTAL_HOST skip them too. if [ -z "${KOLO_STAFF_HOST:-}${KOLO_PORTAL_HOST:-}" ]; then slug=$(printf '%s' "$KOLO_BRAND_NAME" | tr 'A-Z' 'a-z' | sed 's/[^a-z0-9]\{1,\}/-/g; s/^-//' | cut -c1-30 | sed 's/-$//') echo "Kolo gives your install two free addresses: .trykolo.app for customers and -staff.trykolo.app for staff." ask KOLO_ADDRESS_NAME "Name for them, or \"own\" to use your own domains" "$slug" while [ "$KOLO_ADDRESS_NAME" != own ]; do out=$(curl -4 -sS -m 30 -w '\n%{http_code}' --data-urlencode "name=$KOLO_ADDRESS_NAME" "$HUB_URL/api/addresses" 2>&1 || true) code=$(printf '%s' "$out" | tail -n 1); body=$(printf '%s' "$out" | sed '$d') if [ "$code" = 200 ]; then KOLO_PORTAL_HOST=${body% *}; KOLO_STAFF_HOST=${body#* }; echo "addresses: $KOLO_PORTAL_HOST and $KOLO_STAFF_HOST"; break; fi echo "$body" [ -t 0 ] || exit 1 KOLO_ADDRESS_NAME=""; ask KOLO_ADDRESS_NAME "Another name, or \"own\"" done fi ask KOLO_STAFF_HOST "Staff address (a domain pointed at this server)" ask KOLO_PORTAL_HOST "Customer portal address (a domain pointed at this server)" ask KOLO_OWNER_NAME "Your name" ask KOLO_OWNER_EMAIL "Your email, for the first sign-in" ask_opt KOLO_LICENCE "Licence key from your Kolo dashboard (leave empty for a 14-day trial)" CADDY_EMAIL="${CADDY_EMAIL:-$KOLO_OWNER_EMAIL}" KOLO_VERSION="${KOLO_VERSION:-}" # empty: follow the licence's channel (stable until the first release, then stable or early) if ! command -v docker >/dev/null; then echo "installing Docker" command -v curl >/dev/null || { apt-get update -qq && apt-get install -y -qq curl ca-certificates >/dev/null; } curl -fsSL https://get.docker.com | sh >/dev/null fi docker compose version >/dev/null 2>&1 || { echo "Docker is installed but the compose plugin is missing"; exit 1; } mkdir -p "$DIR" && cd "$DIR" && chmod 700 "$DIR" FIRST="${KOLO_VERSION:-latest}" echo "pulling $IMAGE:$FIRST" docker pull -q "$IMAGE:$FIRST" >/dev/null for f in compose.yml Caddyfile kolo; do docker run --rm "$IMAGE:$FIRST" cat "install/$f" > "$f"; done chmod +x kolo && ln -sf "$DIR/kolo" /usr/local/bin/kolo umask 077 cat > .env < Licence. Later: kolo update, kolo backup, kolo doctor, kolo logs. EOF