-
Notifications
You must be signed in to change notification settings - Fork 4
feat: add backup and restore scripts with steps #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 16 commits
724d580
e91479d
9c5855b
07bec37
d300fd3
2c84c46
17af044
9527ecd
5b5f79d
c072d94
b8534a4
8ad6142
18df3c5
96e5ac7
b4b85a6
c3d8a6c
bf0f9dd
3f16a25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| .env | ||
| data/ | ||
| config/ | ||
| .env.backup* | ||
| .DS_Store |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| # Backup and Restore Guide | ||
blockchainluffy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you rename this file to BACKUP_RESTORE.md so that it is clear that it contains the instructions for both? Thank you. |
||
|
|
||
| ## Backup Process | ||
|
|
||
| ### Creating a Backup | ||
|
|
||
| 1. **Ensure services are running** - The backup process requires the database to be accessible | ||
| 2. **Run the backup script**: | ||
| ```bash | ||
| # Use default backup directory (data/backups/) | ||
| ./scripts/backup.sh | ||
|
|
||
| # Use custom backup directory | ||
| ./scripts/backup.sh /path/to/backups | ||
|
|
||
| # Show help | ||
| ./scripts/backup.sh -h | ||
| ``` | ||
| 3. **Backup location** - Backups are stored in the specified directory (default: `data/backups/`) | ||
| 4. **Backup naming** - Files are named with timestamp: `shutter-api-keyper-YYYY-MM-DDTHH-MM-SS.tar.xz` | ||
| 5. **Sanity check** - After the backup completes, verify in keyper logs that it resyncs events | ||
|
|
||
blockchainluffy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ### What Gets Backed Up | ||
|
|
||
| - Database dump (`keyper.dump`) - Contains full schema and data from the `keyper` database | ||
| - Chain data (`data/chain/`) - Blockchain data and configuration | ||
| - Keyper configuration (`config/`) - Application configuration files | ||
| - Environment variables - Except Signing Key | ||
|
|
||
| ### What's NOT Backed Up | ||
|
|
||
| - **Signing Key** - The `SIGNING_KEY` environment variable is intentionally excluded from backups for security reasons. You must manually preserve this value separately. | ||
|
|
||
| ### Security Considerations | ||
|
|
||
| ⚠️ **IMPORTANT**: Backup files contain sensitive information including: | ||
| - Database contents with potentially sensitive data | ||
| - Configuration files that may contain API keys, passwords, or other secrets | ||
| - Chain data that could be used to reconstruct transaction history | ||
|
|
||
| **Security Best Practices:** | ||
| - Store backups on a different machine or secure cloud storage | ||
| - Limit access to backup files to authorized personnel only | ||
| - Consider using backup encryption tools for additional security | ||
|
|
||
| ## Restore Process | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| - **Empty keyper instance** - The restore *must* be performed on a fresh, empty deployment | ||
| - **No running services** - Ensure all Docker containers are stopped before restore | ||
| - **Backup file available** - The backup archive should be present in the specified backup directory | ||
| - **Signing key available** - You must have the original `SIGNING_KEY` value from your deployment | ||
|
|
||
| ### Restore Steps | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is a fresh install, the data dir doesn't exist, so users would need to create it manually.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added command to create data directory, if it does not exist already in restore.sh |
||
|
|
||
| 1. **Setup environment**: | ||
| ```bash | ||
| # Edit the .env file to insert your `SIGNING_KEY`, the rest of the settings will have been restored from the backup. | ||
|
|
||
| 2. **Run restore script**: | ||
| ```bash | ||
| # Use default backup directory (data/backups/) | ||
| ./scripts/restore.sh | ||
|
|
||
| # Use custom backup directory | ||
| ./scripts/restore.sh /path/to/backups | ||
|
|
||
| # Show help | ||
| ./scripts/restore.sh -h | ||
| ``` | ||
| - This will automatically find the latest backup in the specified directory | ||
| - Prompts for confirmation before proceeding | ||
| - Restores all data to appropriate locations | ||
|
|
||
| 3. **Set the Signing Key**: | ||
| - After restoring, update the `.env` file by setting the `SIGNING_KEY` environment variable to the same value used in your original deployment. | ||
| - **CRITICAL**: Without the correct signing key, the restored deployment will not function properly and may not be able to process transactions. | ||
|
|
||
| 4. **Start services**: | ||
| ```bash | ||
| docker compose up -d | ||
| ``` | ||
|
|
||
| ### Restore Locations | ||
|
|
||
| - **Database**: `data/db-dump/keyper.dump` - Automatically restored to PostgreSQL | ||
| - **Chain data**: `data/chain/` - Keyper chain data and configuration | ||
| - **Configuration**: `config/` - Application configuration files | ||
| - **Environment**: `.env` - Updated with restored metrics settings | ||
|
|
||
| ### Important Notes | ||
|
|
||
| - **Database restoration** - The database is automatically restored on first startup via the initialization script | ||
| - **Service order** - Restore must be completed before starting any services | ||
| - **Data integrity** - The restore process overwrites existing data; ensure you have a clean instance | ||
| - **Configuration review** - Review restored configuration files before starting services | ||
| - **Backup directory** - Both scripts accept an optional backup directory parameter, defaulting to `data/backups/` | ||
| - **Incomplete recovery** - Backups do not contain the signing key; manual intervention is required to complete the restore | ||
| - **Security** - Restored data may contain sensitive information; ensure proper access controls are in place | ||
|
|
||
| ### Troubleshooting | ||
|
|
||
| - **No backup found** - Ensure backup files exist in the specified backup directory | ||
| - **Permission errors** - Ensure proper file permissions on backup files and directories | ||
| - **Configuration issues** - Verify that restored configuration files are valid | ||
| - **Custom backup locations** - When using custom backup directories, ensure the path is accessible and writable | ||
| - **Missing signing key** - You should be able to have original signing key at the time of restore. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| R='\033[0;31m' | ||
| G='\033[0;32m' | ||
| Y='\033[0;33m' | ||
| B='\033[0;34m' | ||
| DEF='\033[0m' | ||
|
|
||
| SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) | ||
|
|
||
| # Default backup directory | ||
| DEFAULT_BACKUPS_DIR="${SCRIPT_DIR}/../data/backups" | ||
|
|
||
| # Show usage if help is requested | ||
| if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then | ||
| echo "Usage: $0 [BACKUP_DIRECTORY]" | ||
| echo "" | ||
| echo "Creates a backup archive of the shutter-keyper deployment." | ||
| echo "" | ||
| echo "Arguments:" | ||
| echo " BACKUP_DIRECTORY Directory to store the backup (default: $DEFAULT_BACKUPS_DIR)" | ||
| echo "" | ||
| echo "Examples:" | ||
| echo " $0 # Use default backup directory" | ||
| echo " $0 /path/to/backups # Use custom backup directory" | ||
| echo " $0 -h # Show this help message" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Parse command line arguments | ||
| BACKUPS_DIR="${1:-$DEFAULT_BACKUPS_DIR}" | ||
|
|
||
| ARCHIVE_NAME="shutter-api-keyper-$(date +%Y-%m-%dT%H-%M-%S).tar.xz" | ||
|
|
||
| source "${SCRIPT_DIR}/../.env" | ||
|
|
||
| mkdir -p "$BACKUPS_DIR" | ||
| WORKDIR=$(mktemp -d -p "${BACKUPS_DIR}") | ||
|
|
||
| cleanup_and_restart() { | ||
| rv=$? | ||
| set +e | ||
|
|
||
| rm -rf "$WORKDIR" | ||
| docker compose start | ||
blockchainluffy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if [ $rv -ne 0 ]; then | ||
| echo -e "${R}Unexpected error, exit code: $rv${DEF}" | ||
| fi | ||
|
|
||
| exit $rv | ||
| } | ||
|
|
||
| trap cleanup_and_restart EXIT | ||
|
|
||
| echo -e "${G}Creating backup archive${DEF}" | ||
| echo -e "${B}Backup directory: ${Y}$BACKUPS_DIR${DEF}" | ||
|
|
||
| echo -e "${B}[1/6] Stopping all services except database...${DEF}" | ||
blockchainluffy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| docker compose stop keyper | ||
| docker compose stop chain | ||
|
|
||
| echo -e "${B}[2/6] Creating database dump...${DEF}" | ||
| docker compose exec db pg_dump -U postgres -d keyper -Fc --create --clean -f /var/lib/postgresql/data/keyper.dump | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we also need to delete this dump from the container during the clean and restart exit, to avoid growing the volume with every backup
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the dump file gets overwritten on every backup, anyway I can add command to delete the dump file when backup archive is created |
||
|
|
||
| echo -e "${B}[3/6] Stopping database...${DEF}" | ||
| docker compose stop db | ||
|
|
||
| echo -e "${B}[4/6] Copying data...${DEF}" | ||
| cp -a "${SCRIPT_DIR}/../data/chain/" "${WORKDIR}/chain" | ||
| cp -a "${SCRIPT_DIR}/../data/db/keyper.dump" "${WORKDIR}/keyper.dump" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAICS the source path is wrong
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the backup is created in the db folder only as previous docker setup, would not have new volume mapped in the compose (
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean in case an operator updates to the new version but doesn't run Yeah I guess that's fine. |
||
| cp -a "${SCRIPT_DIR}/../config" "${WORKDIR}/keyper-config" | ||
|
|
||
| mkdir -p "${WORKDIR}/env-config" | ||
| if [ -f "${SCRIPT_DIR}/../.env" ]; then | ||
| sed 's/^SIGNING_KEY=.*/SIGNING_KEY=PLACEHOLDER_REPLACE_WITH_YOUR_PRIVATE_KEY/' "${SCRIPT_DIR}/../.env" > "${WORKDIR}/env-config/.env" | ||
| echo -e "${G}✓ Environment configuration backed up (private key replaced with placeholder)${DEF}" | ||
| else | ||
| echo -e "${Y}⚠ .env file not found, skipping environment backup${DEF}" | ||
| fi | ||
|
|
||
| echo -e "${B}[5/6] Compressing archive...${DEF}" | ||
| docker run --rm -it -v "${WORKDIR}:/workdir" -v "$BACKUPS_DIR:/data" alpine:3.20.1 ash -c "apk -q --no-progress --no-cache add xz pv && tar -cf - -C /workdir . | pv -petabs \$(du -sb /workdir | cut -f 1) | xz -zq > /data/${ARCHIVE_NAME}" | ||
|
|
||
| echo -e "${B}[6/6] Cleaning up...${DEF}" | ||
| rm "${SCRIPT_DIR}/../data/db/keyper.dump" || true | ||
|
|
||
| echo -e "${G}Done, backup archive created at ${B}$BACKUPS_DIR/${ARCHIVE_NAME}${DEF}" | ||
|
|
||
| echo -e "\n\n${R}WARNING, IMPORTANT!${DEF}" | ||
| echo -e "${Y}If you import this backup, make sure to stop this deployment first!${DEF}" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| R='\033[0;31m' | ||
| G='\033[0;32m' | ||
| Y='\033[0;33m' | ||
| B='\033[0;34m' | ||
| DEF='\033[0m' | ||
|
|
||
| SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) | ||
|
|
||
| # Default backup directory | ||
| DEFAULT_BACKUPS_DIR="${SCRIPT_DIR}/../data/backups" | ||
|
|
||
| # Show usage if help is requested | ||
| if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then | ||
| echo "Usage: $0 [BACKUP_DIRECTORY]" | ||
| echo "" | ||
| echo "Restores from the latest backup in the specified directory." | ||
| echo "" | ||
| echo "Arguments:" | ||
| echo " BACKUP_DIRECTORY Directory containing backup files (default: $DEFAULT_BACKUPS_DIR)" | ||
| echo "" | ||
| echo "Examples:" | ||
| echo " $0 # Use default backup directory" | ||
| echo " $0 /path/to/backups # Use custom backup directory" | ||
| echo " $0 -h # Show this help message" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Parse command line arguments | ||
| BACKUPS_DIR="${1:-$DEFAULT_BACKUPS_DIR}" | ||
|
|
||
| WORKDIR=$(mktemp -d -p "${BACKUPS_DIR}") | ||
|
|
||
| cleanup() { | ||
| rv=$? | ||
| set +e | ||
|
|
||
| rm -rf "$WORKDIR" || true | ||
|
|
||
| if [ $rv -ne 0 ]; then | ||
| echo -e "${R}Unexpected error, exit code: $rv${DEF}" | ||
| fi | ||
|
|
||
| exit $rv | ||
| } | ||
|
|
||
| trap cleanup EXIT | ||
|
|
||
| echo -e "${G}Restoring from latest backup${DEF}" | ||
| echo -e "${B}Backup directory: ${Y}$BACKUPS_DIR${DEF}" | ||
|
|
||
| if [ ! -d "$BACKUPS_DIR" ]; then | ||
blockchainluffy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| echo -e "${R}Error: Backups directory not found at $BACKUPS_DIR${DEF}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| LATEST_BACKUP=$(find "$BACKUPS_DIR" -name "shutter-api-keyper-*.tar.xz" -type f | sort | tail -n 1) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not necessary to do anything here now, but a note for future improvement: Some people like to run their containers on really stripped down OSs, therefore it would be more robust to use an ad-hoc container to do these shell actions (i.e. not relying on |
||
|
|
||
| if [ -z "$LATEST_BACKUP" ]; then | ||
| echo -e "${R}Error: No backup files found in $BACKUPS_DIR${DEF}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo -e "${B}Found latest backup: ${Y}$(basename "$LATEST_BACKUP")${DEF}" | ||
|
|
||
| echo -e "${Y}WARNING: This will overwrite existing data!${DEF}" | ||
| read -p "Are you sure you want to continue? (y/N): " -n 1 -r | ||
| echo | ||
| if [[ ! $REPLY =~ ^[Yy]$ ]]; then | ||
| echo -e "${R}Restore cancelled.${DEF}" | ||
| exit 0 | ||
blockchainluffy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| fi | ||
|
|
||
| echo -e "${B}[1/6] Stopping services...${DEF}" | ||
| cd "$SCRIPT_DIR" | ||
| docker compose down | ||
|
|
||
| echo -e "${B}[2/6] Extracting backup archive...${DEF}" | ||
| docker run --rm -v "$LATEST_BACKUP:/backup.tar.xz:ro" -v "$WORKDIR:/extract" alpine:3.20.1 ash -c "apk -q --no-progress --no-cache add xz && tar -xf /backup.tar.xz -C /extract" | ||
|
|
||
| echo -e "${B}[2.5/6] Validating backup contents...${DEF}" | ||
| MISSING_COMPONENTS=() | ||
|
|
||
| if [ ! -d "$WORKDIR/chain" ]; then | ||
| MISSING_COMPONENTS+=("chain data") | ||
| fi | ||
|
|
||
| if [ ! -d "$WORKDIR/keyper-config" ]; then | ||
| MISSING_COMPONENTS+=("keyper configuration") | ||
| fi | ||
|
|
||
| if [ ! -f "$WORKDIR/keyper.dump" ]; then | ||
| MISSING_COMPONENTS+=("database dump") | ||
| fi | ||
|
|
||
| if [ ${#MISSING_COMPONENTS[@]} -gt 0 ]; then | ||
| echo -e "${R}Error: Backup is incomplete. Missing components:${DEF}" | ||
| for component in "${MISSING_COMPONENTS[@]}"; do | ||
| echo -e "${R} - $component${DEF}" | ||
| done | ||
| echo -e "${R}This backup appears to be corrupted or incomplete. Cannot proceed with restore.${DEF}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo -e "${G}✓ Backup validation passed - all required components found${DEF}" | ||
|
|
||
| echo -e "${B}[3/6] Restoring chain data...${DEF}" | ||
| rm -rf "${SCRIPT_DIR}/../data/chain" || true | ||
| mkdir -p "${SCRIPT_DIR}/../data" | ||
| cp -a "$WORKDIR/chain" "${SCRIPT_DIR}/../data/chain" | ||
| echo -e "${G}✓ Chain data restored${DEF}" | ||
|
|
||
| echo -e "${B}[4/6] Restoring keyper configuration...${DEF}" | ||
| rm -rf "${SCRIPT_DIR}/../config" || true | ||
| cp -a "$WORKDIR/keyper-config" "${SCRIPT_DIR}/../config" | ||
| echo -e "${G}✓ Keyper configuration restored${DEF}" | ||
|
|
||
| echo -e "${B}[5/6] Restoring database dump...${DEF}" | ||
| mkdir -p "${SCRIPT_DIR}/../data/db-dump" | ||
| cp "$WORKDIR/keyper.dump" "${SCRIPT_DIR}/../data/db-dump/keyper.dump" | ||
| echo -e "${G}✓ Database dump restored${DEF}" | ||
|
|
||
| echo -e "${B}[6/6] Restoring environment configuration...${DEF}" | ||
| if [ -f "$WORKDIR/env-config/.env" ]; then | ||
| if [ -f "${SCRIPT_DIR}/../.env" ]; then | ||
| cp "${SCRIPT_DIR}/../.env" "${SCRIPT_DIR}/../.env.backup.$(date +%Y%m%d_%H%M%S)" | ||
| fi | ||
|
|
||
| cp "$WORKDIR/env-config/.env" "${SCRIPT_DIR}/../.env" | ||
| echo -e "${G}✓ Environment configuration restored${DEF}" | ||
| echo -e "${Y}⚠ You'll need to set your SIGNING_KEY manually${DEF}" | ||
| else | ||
| echo -e "${Y}⚠ No env-config/.env found in backup${DEF}" | ||
| fi | ||
|
|
||
| echo -e "${B}Cleaning up...${DEF}" | ||
| rm -rf "$WORKDIR" | ||
|
|
||
| echo -e "${G}Restore completed successfully!${DEF}" | ||
| echo -e "${Y}Next steps:${DEF}" | ||
| echo -e "1. Review the restored configuration files" | ||
| echo -e "2. Start the services: ${B}docker compose up -d${DEF}" | ||
blockchainluffy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| echo -e "3. The database will be automatically restored on first startup" | ||
|
|
||
| trap - EXIT | ||
Uh oh!
There was an error while loading. Please reload this page.