fix: improve install.sh on macOS and linux

This commit is contained in:
Gareth
2026-05-03 21:52:09 -07:00
parent 50533716b1
commit cd6c5ed5f3
2 changed files with 237 additions and 138 deletions
+54 -118
View File
@@ -59,28 +59,67 @@ Built with Go, Backrest is distributed as a standalone, lightweight binary with
# Installation
Backrest is packaged as a single executable. It can be run directly on Linux, macOS, and Windows. [restic](https://github.com/restic/restic) will be downloaded and installed on first run.
Backrest is packaged as a single executable. It runs directly on Linux, macOS, and Windows. [restic](https://github.com/restic/restic) is downloaded automatically on first run.
Once installed, access Backrest at `http://localhost:9898` (default port). First-time setup will prompt for username and password creation.
> [!NOTE]
> To change the default port, set the `BACKREST_PORT` environment variable (e.g., `BACKREST_PORT=0.0.0.0:9898` to listen on all interfaces)
>
> To change the default port, set the `BACKREST_PORT` environment variable (e.g., `BACKREST_PORT=0.0.0.0:9898` to listen on all interfaces). The install script accepts `--allow-remote-access` as a shortcut for this.
>
> Backrest will use your system's installed version of restic if it's available and compatible. If not, Backrest will download and install a suitable version in its data directory, keeping it updated. To use a specific restic binary, set the `BACKREST_RESTIC_COMMAND` environment variable to the desired path.
### Quick Start Options
## Linux & macOS (Recommended)
1. **Pre-built Release**: Download from the [releases page](https://github.com/garethgeorge/backrest/releases)
2. **Docker**: Use `ghcr.io/garethgeorge/backrest:latest` (also available on [Docker Hub](https://hub.docker.com/r/garethgeorge/backrest))
- Includes rclone and common Unix utilities
- For minimal image, use `ghcr.io/garethgeorge/backrest:scratch`
3. **Build from Source**: See [Development](#development) section below
The install script downloads the latest release, drops the binary into `/usr/local/bin`, and sets up the appropriate auto-start integration (systemd or OpenRC on Linux; launchd on macOS):
### Running with Docker Compose
```sh
curl -fsSL https://raw.githubusercontent.com/garethgeorge/backrest/main/install.sh | sudo bash
```
Docker image: `ghcr.io/garethgeorge/backrest`
Flags go after `--`:
Example compose file:
```sh
# Bind to all interfaces (default: 127.0.0.1:9898)
curl -fsSL https://raw.githubusercontent.com/garethgeorge/backrest/main/install.sh | sudo bash -s -- --allow-remote-access
# Uninstall (removes service, autostart entry, and /usr/local/bin/backrest)
curl -fsSL https://raw.githubusercontent.com/garethgeorge/backrest/main/install.sh | sudo bash -s -- --uninstall
```
The service runs as your user by default (so config and data live under your `$HOME`). To install as `root` instead, pass `--root`. After install, access Backrest at `http://localhost:9898`.
> [!TIP]
> Review [install.sh](./install.sh) before piping it into a shell. You can also clone the repo and run `./install.sh` locally; it accepts the same flags.
### macOS — Homebrew (alternative)
[Homebrew tap](https://github.com/garethgeorge/homebrew-backrest-tap):
```sh
brew tap garethgeorge/homebrew-backrest-tap
brew install backrest
brew services start backrest
```
> [!NOTE]
> You may need to grant Full Disk Access to Backrest. Go to `System Preferences > Security & Privacy > Privacy > Full Disk Access` and add `/usr/local/bin/backrest`.
### Arch Linux (AUR)
[Backrest on AUR](https://aur.archlinux.org/packages/backrest) is third-party (not maintained by the Backrest project) and tweaks the systemd unit; see the [AUR service file](https://aur.archlinux.org/cgit/aur.git/tree/backrest@.service?h=backrest) for details.
```sh
paru -Sy backrest # or: yay -Sy backrest
sudo systemctl enable --now backrest@$USER.service
```
## Docker
Image: `ghcr.io/garethgeorge/backrest` (also on [Docker Hub](https://hub.docker.com/r/garethgeorge/backrest)).
- Includes rclone and common Unix utilities
- For a minimal image, use `ghcr.io/garethgeorge/backrest:scratch`
### Docker Compose
```yaml
version: "3.8"
@@ -108,115 +147,12 @@ services:
restart: unless-stopped
```
### Linux
## Windows
#### Option A: Install Script (Recommended)
```sh
mkdir backrest && tar -xzvf backrest_Linux_x86_64.tar.gz -C backrest
cd backrest && ./install.sh
```
This script will:
- Move the Backrest binary to `/usr/local/bin`
- Create and start a systemd service running as the current user (use `sudo ./install.sh` to install as root)
#### Option B: Manual Installation with systemd
```sh
sudo mv backrest /usr/local/bin/backrest
sudo tee /etc/systemd/system/backrest.service > /dev/null <<EOT
[Unit]
Description=Backrest
After=network.target
[Service]
Type=simple
User=$(whoami)
ExecStart=/usr/local/bin/backrest
Environment="BACKREST_PORT=127.0.0.1:9898"
[Install]
WantedBy=multi-user.target
EOT
sudo systemctl enable --now backrest
```
#### Option C: Using cron (Basic)
```sh
sudo mv backrest /usr/local/bin/backrest
(crontab -l 2>/dev/null; echo "@reboot /usr/local/bin/backrest") | crontab -
```
**Verify Installation**
- Access Backrest at `http://localhost:9898`
- For the systemd service: `sudo systemctl status backrest`
> [!NOTE]
> Adjust the `User` in the systemd service file if needed. The install script and manual systemd instructions use your current user by default.
>
> By default backrest listens only on localhost, you can open optionally open it up to remote connections by setting the `BACKREST_PORT` environment variable. For systemd installations, run `sudo systemctl edit backrest` and add:
> ```
> [Service]
> Environment="BACKREST_PORT=0.0.0.0:9898"
> ```
> Using `0.0.0.0` allows connections from any interface.
#### Arch Linux
> [!Note]
> [Backrest on AUR](https://aur.archlinux.org/packages/backrest) is not maintained by the Backrest official and has made minor adjustments to the recommended services. Please refer to [here](https://aur.archlinux.org/cgit/aur.git/tree/backrest@.service?h=backrest) for details. In [backrest@.service](https://aur.archlinux.org/cgit/aur.git/tree/backrest@.service?h=backrest), use `restic` from the Arch Linux official repository by setting `BACKREST_RESTIC_COMMAND`. And for information on enable/starting/stopping services, please refer to [Systemd#Using_units](https://wiki.archlinux.org/title/Systemd#Using_units).
```shell
## Install Backrest from AUR
paru -Sy backrest # or: yay -Sy backrest
## Enable Backrest service for current user
sudo systemctl enable --now backrest@$USER.service
```
### macOS
#### Homebrew (Recommended)
Backrest is available via a [Homebrew tap](https://github.com/garethgeorge/homebrew-backrest-tap):
```sh
brew tap garethgeorge/homebrew-backrest-tap
brew install backrest
brew services start backrest
```
This method uses [Brew Services](https://github.com/Homebrew/homebrew-services) to manage Backrest. It will launch on startup and run on port 127.0.0.1:9898 by default.
> [!NOTE]
> You may need to grant Full Disk Access to Backrest. Go to `System Preferences > Security & Privacy > Privacy > Full Disk Access` and add `/usr/local/bin/backrest`.
#### Manual Installation
1. Download the latest Darwin release from the [releases page](https://github.com/garethgeorge/backrest/releases).
2. Extract and install:
```sh
mkdir backrest && tar -xzvf backrest_Darwin_arm64.tar.gz -C backrest
cd backrest && ./install.sh
```
The install script will:
- Move the Backrest binary to `/usr/local/bin`
- Create a launch agent at `~/Library/LaunchAgents/com.backrest.plist`
- Load the launch agent
Download the Windows installer for your architecture from the [releases page](https://github.com/garethgeorge/backrest/releases). The installer, named `Backrest-setup-[arch].exe`, places Backrest and a GUI tray application in `%localappdata%\Programs\Backrest\`. The tray application, set to start on login, monitors Backrest.
> [!TIP]
> Review the script before running to ensure you're comfortable with its operations.
### Windows
Download the Windows installer for your architecture from the [releases page](https://github.com/garethgeorge/backrest/releases). The installer, named Backrest-setup-[arch].exe, will place Backrest and a GUI tray application in `%localappdata%\Programs\Backrest\`. The tray application, set to start on login, monitors Backrest.
> [!TIP]
> To override the default port before installation, set a user environment variable named BACKREST_PORT. On Windows 10+, navigate to Settings > About > Advanced system settings > Environment Variables. Under "User variables", create a new variable `BACKREST_PORT` with the value "127.0.0.1:port" (e.g., "127.0.0.1:8080" for port 8080). If changing post-installation, re-run the installer to update shortcuts with the new port.
> To override the default port before installation, set a user environment variable named `BACKREST_PORT`. On Windows 10+, navigate to Settings > About > Advanced system settings > Environment Variables. Under "User variables", create a new variable `BACKREST_PORT` with the value `127.0.0.1:port` (e.g. `127.0.0.1:8080`). If changing post-installation, re-run the installer to update shortcuts with the new port.
---
+183 -20
View File
@@ -1,7 +1,15 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Detect curl-piped invocation (e.g. `curl ... | bash` or `curl ... | sudo bash`).
# Under that mode $0 is "bash" rather than a path to a real script file.
if [ -f "$0" ]; then
PIPED_INVOCATION=false
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
else
PIPED_INVOCATION=true
SCRIPT_DIR=""
fi
# --- Defaults ---
ALLOW_REMOTE_ACCESS=false
@@ -9,6 +17,7 @@ INSTALL_MODE="" # "tray", "service", or "" (auto-detect)
UNINSTALL_ONLY=false
ACQUISITION_MODE="" # "local", "source", "release", or "" (auto-detect)
RELEASE_BINARY="" # set by acquire_binary in release mode
ROOT_INSTALL=false # acknowledged install as root (services run as root)
# --- Parse arguments ---
for arg in "$@"; do
@@ -34,9 +43,14 @@ for arg in "$@"; do
--from-release)
ACQUISITION_MODE="release"
;;
--root)
ROOT_INSTALL=true
;;
*)
progname="$0"
[ "$PIPED_INVOCATION" = true ] && progname="install.sh"
echo "Unknown option: $arg"
echo "Usage: $0 [OPTIONS]"
echo "Usage: $progname [OPTIONS]"
echo ""
echo "Options:"
echo " --uninstall Uninstall backrest (remove all artifacts, don't reinstall)"
@@ -46,11 +60,74 @@ for arg in "$@"; do
echo " --from-local Use ./backrest binary in script directory (default if present)"
echo " --from-source Build from source"
echo " --from-release Download latest release from GitHub"
echo " --root Acknowledge install as root (services run as root)"
echo ""
echo "Curl-piped invocation:"
echo " curl -fsSL https://raw.githubusercontent.com/garethgeorge/backrest/main/install.sh | sudo bash"
echo " curl -fsSL https://raw.githubusercontent.com/garethgeorge/backrest/main/install.sh | sudo bash -s -- --no-tray"
exit 1
;;
esac
done
# --- Curl-piped invocation guards ---
if [ "$PIPED_INVOCATION" = true ]; then
case "$ACQUISITION_MODE" in
local|source)
echo "Error: --from-$ACQUISITION_MODE is not supported when running via 'curl | bash';"
echo "the script has no on-disk location to read from. Either drop the flag"
echo "(downloads the latest release from GitHub) or download install.sh to disk first."
exit 1
;;
esac
# Force release acquisition; SCRIPT_DIR is empty so local detection wouldn't work anyway.
if [ -z "$ACQUISITION_MODE" ]; then
ACQUISITION_MODE="release"
fi
fi
# --- Determine the install user (whose name goes into service units / autostart) ---
# When invoked via sudo, $(whoami) is "root" but $SUDO_USER points at the real user;
# we want services owned by that user, not root. Bare-root installs require --root.
if [ "$(id -u)" -eq 0 ]; then
if [ "$ROOT_INSTALL" = true ]; then
INSTALL_USER="root"
INSTALL_HOME="/root"
elif [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != "root" ]; then
INSTALL_USER="$SUDO_USER"
INSTALL_HOME="$(getent passwd "$SUDO_USER" 2>/dev/null | cut -d: -f6)"
[ -z "$INSTALL_HOME" ] && INSTALL_HOME="/home/$SUDO_USER"
else
echo "Error: this script is being run as root directly."
echo ""
echo "Running as root means backrest will run as root and store its config"
echo "and data under /root, which is rarely what you want. Either:"
echo " - Re-run as your normal user (sudo will be invoked when needed), or"
echo " - Pass --root to acknowledge that you really want a root install."
exit 1
fi
else
INSTALL_USER="$(whoami)"
INSTALL_HOME="$HOME"
fi
# Resolve the install user's XDG config dir. Don't honor $XDG_CONFIG_HOME from
# the current env when running via sudo -- that value belongs to root.
if [ "$(id -u)" -eq 0 ] && [ "$INSTALL_USER" != "root" ]; then
INSTALL_XDG_CONFIG_HOME="$INSTALL_HOME/.config"
else
INSTALL_XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$INSTALL_HOME/.config}"
fi
# Run a command as the install user (drop privileges if currently root).
run_as_install_user() {
if [ "$(id -u)" -eq 0 ] && [ "$INSTALL_USER" != "root" ]; then
sudo -u "$INSTALL_USER" "$@"
else
"$@"
fi
}
# --- Bind address ---
if [ "$ALLOW_REMOTE_ACCESS" = true ]; then
BACKREST_PORT="0.0.0.0:9898"
@@ -58,6 +135,32 @@ else
BACKREST_PORT="127.0.0.1:9898"
fi
# --- Pre-authenticate sudo so subsequent calls don't prompt mid-install ---
# When piped from curl, stdin is occupied by the script body, so sudo can't read
# a password from it. We force a TTY prompt up front; if no TTY is available
# (e.g. fully non-interactive shell), fail clearly with the canonical fix.
if [ "$(id -u)" -ne 0 ]; then
if ! sudo -n true 2>/dev/null; then
if [ -t 0 ] || [ -t 1 ] || [ -r /dev/tty ]; then
echo "Caching sudo credentials (you may be prompted for your password)..."
if [ -r /dev/tty ]; then
sudo -v </dev/tty
else
sudo -v
fi
else
echo "Error: this script needs sudo, but no TTY is available to prompt for a password."
if [ "$PIPED_INVOCATION" = true ]; then
echo "Re-run with sudo on the outside, e.g.:"
echo " curl -fsSL https://raw.githubusercontent.com/garethgeorge/backrest/main/install.sh | sudo bash"
else
echo "Run 'sudo -v' first, or invoke the script with sudo."
fi
exit 1
fi
fi
fi
# =============================================================================
# Detection helpers
# =============================================================================
@@ -77,7 +180,7 @@ detect_previous_mode() {
echo "openrc"
elif [ -f /Library/LaunchAgents/com.backrest.plist ]; then
echo "launchd"
elif [ -f "${XDG_CONFIG_HOME:-$HOME/.config}/autostart/backrest.desktop" ]; then
elif [ -f "$INSTALL_XDG_CONFIG_HOME/autostart/backrest.desktop" ]; then
echo "tray"
else
echo ""
@@ -126,6 +229,20 @@ remove_openrc_service() {
remove_launchd_plist() {
if [ -f /Library/LaunchAgents/com.backrest.plist ]; then
local uid prefix
uid="$(id -u "$(logname 2>/dev/null || echo "$USER")")"
if [ "$(id -u)" -eq 0 ]; then
prefix="sudo -u #$uid "
else
prefix=""
fi
echo "Tearing down launchd agent:"
echo " ${prefix}launchctl bootout gui/$uid/com.backrest"
if [ "$(id -u)" -eq 0 ]; then
sudo -u "#$uid" launchctl bootout "gui/$uid/com.backrest" 2>/dev/null || true
else
launchctl bootout "gui/$uid/com.backrest" 2>/dev/null || true
fi
launchctl unload /Library/LaunchAgents/com.backrest.plist 2>/dev/null || true
sudo rm -f /Library/LaunchAgents/com.backrest.plist
echo "Removed launchd plist"
@@ -133,9 +250,9 @@ remove_launchd_plist() {
}
remove_autostart_desktop() {
local desktop_file="${XDG_CONFIG_HOME:-$HOME/.config}/autostart/backrest.desktop"
local desktop_file="$INSTALL_XDG_CONFIG_HOME/autostart/backrest.desktop"
if [ -f "$desktop_file" ]; then
rm -f "$desktop_file"
run_as_install_user rm -f "$desktop_file"
echo "Removed tray autostart entry"
fi
}
@@ -153,7 +270,7 @@ remove_all() {
remove_openrc_service
remove_launchd_plist
remove_autostart_desktop
pkill -f "backrest.*--tray" 2>/dev/null || true
run_as_install_user pkill -f "backrest.*--tray" 2>/dev/null || true
remove_binary
}
@@ -275,8 +392,8 @@ After=network.target
[Service]
Type=simple
User=$(whoami)
Group=$(whoami)
User=${INSTALL_USER}
Group=${INSTALL_USER}
ExecStart=/usr/local/bin/backrest
Restart=on-failure
Environment="BACKREST_PORT=$BACKREST_PORT"
@@ -298,13 +415,12 @@ depend() {
use net logger
}
: \${BACKREST_PORT:=${BACKREST_PORT}}
export BACKREST_PORT="${BACKREST_PORT}"
command=/usr/local/bin/backrest
command_background=true
command_args="-bind-address \${BACKREST_PORT}"
pidfile="/run/\${RC_SVCNAME}.pid"
command_user="$(whoami):$(whoami)"
command_user="${INSTALL_USER}:${INSTALL_USER}"
supervisor=supervise-daemon
EOM
@@ -312,11 +428,11 @@ EOM
}
create_autostart_desktop() {
local autostart_dir="${XDG_CONFIG_HOME:-$HOME/.config}/autostart"
local autostart_dir="$INSTALL_XDG_CONFIG_HOME/autostart"
local desktop_file="$autostart_dir/backrest.desktop"
mkdir -p "$autostart_dir"
cat >"$desktop_file" <<-EOM
run_as_install_user mkdir -p "$autostart_dir"
run_as_install_user tee "$desktop_file" >/dev/null <<-EOM
[Desktop Entry]
Name=Backrest
Comment=Backrest backup manager tray applet
@@ -357,9 +473,37 @@ EOM
}
enable_launchd_plist() {
launchctl unload /Library/LaunchAgents/com.backrest.plist 2>/dev/null || true
launchctl load -w /Library/LaunchAgents/com.backrest.plist
echo "Loaded launchd plist"
local uid target prefix
uid="$(id -u "$(logname 2>/dev/null || echo "$USER")")"
target="gui/$uid"
if [ "$(id -u)" -eq 0 ]; then
prefix="sudo -u #$uid "
else
prefix=""
fi
echo "Bootstrapping launchd agent into $target:"
echo " ${prefix}launchctl bootout $target/com.backrest # tear down any existing instance"
echo " ${prefix}launchctl bootstrap $target /Library/LaunchAgents/com.backrest.plist"
echo " ${prefix}launchctl kickstart -k $target/com.backrest"
if [ "$(id -u)" -eq 0 ]; then
sudo -u "#$uid" launchctl bootout "$target/com.backrest" 2>/dev/null || true
sudo -u "#$uid" launchctl bootstrap "$target" /Library/LaunchAgents/com.backrest.plist
sudo -u "#$uid" launchctl kickstart -k "$target/com.backrest"
else
launchctl bootout "$target/com.backrest" 2>/dev/null || true
launchctl bootstrap "$target" /Library/LaunchAgents/com.backrest.plist
launchctl kickstart -k "$target/com.backrest"
fi
echo "Loaded and started launchd agent"
echo ""
echo "To manually stop/unload later:"
echo " ${prefix}launchctl bootout $target/com.backrest"
echo "To manually start/load again:"
echo " ${prefix}launchctl bootstrap $target /Library/LaunchAgents/com.backrest.plist"
echo " ${prefix}launchctl kickstart -k $target/com.backrest"
}
# =============================================================================
@@ -376,7 +520,24 @@ install_linux() {
echo ""
echo "Backrest installed in tray mode."
echo "It will start automatically when you next log in to your desktop session."
echo "To start it now, run: /usr/local/bin/backrest --tray &"
# Try to start it now in the background so the user doesn't have to log out/in.
# This only works if we have access to the user's graphical session; if not,
# the autostart entry will still pick it up on next login.
if [ "$(id -u)" -eq 0 ] && [ "$INSTALL_USER" != "root" ]; then
echo "Starting backrest tray as $INSTALL_USER..."
sudo -u "$INSTALL_USER" \
env BACKREST_PORT="$BACKREST_PORT" \
nohup /usr/local/bin/backrest --tray >/dev/null 2>&1 &
disown 2>/dev/null || true
echo "(If the tray icon doesn't appear, your graphical session may not be"
echo " reachable from this shell -- log out/in and the autostart entry will"
echo " launch it. Or run manually: /usr/local/bin/backrest --tray &)"
else
echo "Starting backrest tray..."
BACKREST_PORT="$BACKREST_PORT" \
nohup /usr/local/bin/backrest --tray >/dev/null 2>&1 &
disown 2>/dev/null || true
fi
;;
service)
install_binary
@@ -444,8 +605,10 @@ if [ "$UNINSTALL_ONLY" = true ]; then
fi
# Step 3: Determine acquisition mode
# (Already forced to "release" earlier under PIPED_INVOCATION, so SCRIPT_DIR is
# guaranteed non-empty when we hit the local-detect branch below.)
if [ -z "$ACQUISITION_MODE" ]; then
if [ -f "$SCRIPT_DIR/backrest" ]; then
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/backrest" ]; then
ACQUISITION_MODE="local"
else
ACQUISITION_MODE="release"
@@ -472,7 +635,7 @@ elif [ -z "$INSTALL_MODE" ]; then
# echo "Use --no-tray to install as a system service instead."
else
INSTALL_MODE="service"
echo "Defaulting to system service install. Use --experimental-linux-tray to install as a tray app."
echo "Defaulting to system service install."
fi
EFFECTIVE_MODE="$INSTALL_MODE"
else