#!/bin/bash # Kanboard restore — companion to backup.sh (kb#192). # # Restores a snapshot produced by backup.sh (db.sqlite + optional plugins.tar.gz) # into a running Kanboard container. Defaults to the live "kanboard" container/ # volumes, but every target is overridable via env vars so the exact same script # can be pointed at a disposable/throwaway container for a dry-run restore test # (see kb#192 runbook for the recommended throwaway-container recipe). # # Usage: # ./restore.sh /mnt/backups/kanboard/ # # Env overrides (defaults = live service): # CONTAINER=kanboard # target container name # DATA_PATH=/var/www/app/data # data dir inside the container # PLUGINS_PATH=/var/www/app/plugins # plugins dir inside the container # # WARNING: this overwrites the target container's live database. Never run # against the "kanboard" container name unless you intend a real disaster # recovery — for testing, point CONTAINER at a throwaway container instead. set -euo pipefail CONTAINER="${CONTAINER:-kanboard}" DATA_PATH="${DATA_PATH:-/var/www/app/data}" PLUGINS_PATH="${PLUGINS_PATH:-/var/www/app/plugins}" if [ $# -lt 1 ]; then echo "Usage: $0 " >&2 echo " e.g. $0 /mnt/backups/kanboard/20260728-0300" >&2 exit 1 fi SRC="$(realpath "$1")" DB_FILE="$SRC/db.sqlite" PLUGINS_FILE="$SRC/plugins.tar.gz" if [ ! -f "$DB_FILE" ]; then echo "Error: $DB_FILE not found" >&2 exit 1 fi if ! docker inspect "$CONTAINER" > /dev/null 2>&1; then echo "Error: container '$CONTAINER' does not exist" >&2 exit 1 fi echo "Restoring into container '$CONTAINER' from $SRC" # Stop the app so the sqlite file isn't being written to concurrently. docker stop "$CONTAINER" > /dev/null # Replace the database file. docker cp "$DB_FILE" "$CONTAINER:$DATA_PATH/db.sqlite" # Restore plugins, if present in the snapshot. if [ -f "$PLUGINS_FILE" ]; then docker cp "$PLUGINS_FILE" "$CONTAINER:/tmp/plugins.tar.gz" docker start "$CONTAINER" > /dev/null # Extract inside the container so ownership matches what the app expects. docker exec "$CONTAINER" sh -c "rm -rf '$PLUGINS_PATH'/* && tar -xzf /tmp/plugins.tar.gz -C '$PLUGINS_PATH' && rm -f /tmp/plugins.tar.gz" else echo "Note: no plugins.tar.gz in snapshot, skipping plugin restore" docker start "$CONTAINER" > /dev/null fi echo "Waiting for Kanboard to come up..." for i in $(seq 1 30); do if docker exec "$CONTAINER" php -r 'exit(file_exists("'"$DATA_PATH"'/db.sqlite") ? 0 : 1);' > /dev/null 2>&1; then break fi sleep 1 done echo "Verifying restored database..." docker exec "$CONTAINER" php -r ' $db = new PDO("sqlite:'"$DATA_PATH"'/db.sqlite"); $count = $db->query("SELECT COUNT(*) FROM tasks")->fetchColumn(); echo "tasks table row count: $count\n"; ' echo "Restore complete: $CONTAINER"