-
Notifications
You must be signed in to change notification settings - Fork 3
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 5 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 |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| # 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 | ||
| ./scripts/backup.sh | ||
| ``` | ||
| 3. **Backup location** - Backups are stored in `data/backups/` directory | ||
| 4. **Backup naming** - Files are named with timestamp: `shutter-api-keyper-YYYY-MM-DDTHH-MM-SS.tar.xz` | ||
|
|
||
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 | ||
|
|
||
| ## 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 `data/backups/` directory | ||
|
|
||
| ### 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 | ||
| cp example-api.env .env | ||
blockchainluffy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # Edit .env with your configuration values | ||
blockchainluffy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ``` | ||
|
|
||
| 2. **Run restore script**: | ||
| ```bash | ||
| ./scripts/restore.sh | ||
| ``` | ||
| - This will automatically find the latest backup in `data/backups/` | ||
| - 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. | ||
|
|
||
| 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 | ||
|
|
||
| ### Troubleshooting | ||
|
|
||
| - **No backup found** - Ensure backup files exist in `data/backups/` directory | ||
| - **Permission errors** - Ensure proper file permissions on backup files | ||
| - **Configuration issues** - Verify that restored configuration files are valid | ||
blockchainluffy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| #!/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 ) | ||
| ARCHIVE_NAME="shutter-api-keyper-$(date +%Y-%m-%dT%H-%M-%S).tar.xz" | ||
|
|
||
| source "${SCRIPT_DIR}/../.env" | ||
|
|
||
| WORKDIR=$(mktemp -d) | ||
blockchainluffy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| cleanup() { | ||
| rv=$? | ||
| set +e | ||
| echo -e "${R}Unexpected error, exit code: $rv, cleaning up.${DEF}" | ||
| docker compose unpause || true | ||
| exit $rv | ||
| } | ||
|
|
||
| trap cleanup EXIT | ||
|
|
||
| echo -e "${G}Creating backup archive${DEF}" | ||
|
|
||
| mkdir -p "${SCRIPT_DIR}/../data/backups" | ||
|
|
||
| echo -e "${B}[1/7] Pausing all services except database...${DEF}" | ||
| docker compose pause keyper || true | ||
|
||
| docker compose pause chain || true | ||
|
|
||
| echo -e "${B}[2/7] 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/7] Pausing database...${DEF}" | ||
| docker compose pause db || true | ||
|
|
||
| echo -e "${B}[4/7] 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" | ||
| # Copy the entire .env file but replace the private key value with a placeholder | ||
| 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/7] Resuming services...${DEF}" | ||
| docker compose unpause || true | ||
|
|
||
| echo -e "${B}[6/7] Compressing archive...${DEF}" | ||
| docker run --rm -it -v "${WORKDIR}:/workdir" -v "${SCRIPT_DIR}/../data:/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/backups/${ARCHIVE_NAME}" | ||
|
|
||
| echo -e "${B}[7/7] Cleaning up...${DEF}" | ||
| rm -rf "$WORKDIR" | ||
|
|
||
| echo -e "${G}Done, backup archive created at ${B}data/backups/${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}" | ||
|
|
||
| trap - EXIT | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| #!/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 ) | ||
| BACKUPS_DIR="${SCRIPT_DIR}/../data/backups" | ||
|
|
||
| WORKDIR=$(mktemp -d) | ||
blockchainluffy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| cleanup() { | ||
| rv=$? | ||
| set +e | ||
| echo -e "${R}Unexpected error, exit code: $rv, cleaning up.${DEF}" | ||
| rm -rf "$WORKDIR" || true | ||
| exit $rv | ||
| } | ||
|
|
||
| trap cleanup EXIT | ||
|
|
||
| echo -e "${G}Restoring from latest backup${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}" | ||
| docker compose down || true | ||
|
|
||
| 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}[3/6] Restoring chain data...${DEF}" | ||
| if [ -d "$WORKDIR/chain" ]; then | ||
blockchainluffy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| mkdir -p "${SCRIPT_DIR}/../data/chain" | ||
|
||
| rm -rf "${SCRIPT_DIR}/../data/chain" | ||
| cp -a "$WORKDIR/chain" "${SCRIPT_DIR}/../data/chain" | ||
| echo -e "${G}✓ Chain data restored${DEF}" | ||
| else | ||
| echo -e "${Y}⚠ No chain data found in backup${DEF}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo -e "${B}[4/6] Restoring keyper configuration...${DEF}" | ||
| if [ -d "$WORKDIR/keyper-config" ]; then | ||
| mkdir -p "${SCRIPT_DIR}/../config" | ||
| rm -rf "${SCRIPT_DIR}/../config" | ||
| cp -a "$WORKDIR/keyper-config" "${SCRIPT_DIR}/../config" | ||
| echo -e "${G}✓ Keyper configuration restored${DEF}" | ||
| else | ||
| echo -e "${Y}⚠ No keyper-config found in backup${DEF}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo -e "${B}[5/6] Restoring database dump...${DEF}" | ||
| if [ -f "$WORKDIR/keyper.dump" ]; then | ||
| 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}" | ||
| else | ||
| echo -e "${Y}⚠ No database dump found in backup${DEF}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| 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)" | ||
|
|
||
| CURRENT_SIGNING_KEY=$(grep '^SIGNING_KEY=' "${SCRIPT_DIR}/../.env" 2>/dev/null || echo "") | ||
|
|
||
| cp "$WORKDIR/env-config/.env" "${SCRIPT_DIR}/../.env" | ||
|
|
||
| if [ -n "$CURRENT_SIGNING_KEY" ]; then | ||
| echo "$CURRENT_SIGNING_KEY" >> "${SCRIPT_DIR}/../.env" | ||
| fi | ||
|
||
|
|
||
| echo -e "${G}✓ Environment configuration restored (private key preserved)${DEF}" | ||
| else | ||
| cp "$WORKDIR/env-config/.env" "${SCRIPT_DIR}/../.env" | ||
| echo -e "${G}✓ Environment configuration restored${DEF}" | ||
| echo -e "${Y}⚠ No existing SIGNING_KEY found, you'll need to set it manually${DEF}" | ||
| fi | ||
| 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.