This commit is contained in:
Jake Kasper
2025-08-20 14:18:47 -04:00
parent 63fe21d3e9
commit 402b67a2b3
2 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
# Portainer Stack Backup Script
This script connects to a Portainer instance and exports all stacks with their configurations, environment variables, and docker-compose files.
## Prerequisites
- `curl` (usually pre-installed on macOS)
- `jq` for JSON processing: `brew install jq`
## Usage
1. Make sure the script is executable:
```bash
chmod +x portainer-backup.sh
```
2. Run the script:
```bash
./portainer-backup.sh
```
3. When prompted, enter:
- Portainer URL (e.g., `http://localhost:9000`)
- Username
- Password
Alternatively, you can edit the script and set these variables at the top:
```bash
PORTAINER_URL="http://your-portainer-url:9000"
USERNAME="your-username"
PASSWORD="your-password"
```
## Output
The script creates a `portainer-stacks-backup` directory containing:
```
portainer-stacks-backup/
├── stack-name-1/
│ ├── stack-info.json # Complete stack information
│ ├── environment-variables.json # Environment variables as JSON
│ ├── .env # Environment variables in .env format
│ ├── docker-compose.yml # Stack's compose file
│ └── metadata.txt # Human-readable metadata
├── stack-name-2/
│ └── ...
```
## What gets backed up
For each stack:
- Complete stack configuration (JSON format)
- Environment variables (both JSON and .env formats)
- Docker Compose file content
- Metadata (creation date, status, type, etc.)
## Error Handling
The script includes error handling for:
- Missing dependencies
- Authentication failures
- Network connectivity issues
- API errors
## Security Note
The script handles authentication via JWT tokens. Passwords are prompted securely (hidden input) if not set in the script.

232
Backup_Script/portainer-backup.sh Executable file
View File

@@ -0,0 +1,232 @@
#!/bin/bash
# Portainer Stack Backup Script
# This script connects to a Portainer instance and exports all stacks with their configurations
set -e
# Configuration - Update these variables
PORTAINER_URL="http://192.168.11.12:9000" # Change this to your Portainer URL
USERNAME="admin" # Change this to your username
PASSWORD="JohnWayne#21" # Change this to your password or leave empty for prompt
OUTPUT_DIR="portainer-stacks-backup"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Function to check if required tools are installed
check_dependencies() {
print_status "Checking dependencies..."
if ! command -v curl &> /dev/null; then
print_error "curl is required but not installed."
exit 1
fi
if ! command -v jq &> /dev/null; then
print_error "jq is required but not installed. Please install it with: brew install jq"
exit 1
fi
print_status "All dependencies are available."
}
# Function to prompt for configuration if not set
get_config() {
if [ -z "$PORTAINER_URL" ]; then
read -p "Enter Portainer URL (e.g., http://localhost:9000): " PORTAINER_URL
fi
if [ -z "$USERNAME" ]; then
read -p "Enter Portainer username: " USERNAME
fi
if [ -z "$PASSWORD" ]; then
read -s -p "Enter Portainer password: " PASSWORD
echo
fi
# Remove trailing slash from URL if present
PORTAINER_URL=$(echo "$PORTAINER_URL" | sed 's/\/$//g')
}
# Function to authenticate with Portainer
authenticate() {
print_status "Authenticating with Portainer..."
AUTH_RESPONSE=$(curl -s -X POST \
-H "Content-Type: application/json" \
-d "{\"username\":\"$USERNAME\",\"password\":\"$PASSWORD\"}" \
"$PORTAINER_URL/api/auth")
if [ $? -ne 0 ]; then
print_error "Failed to connect to Portainer at $PORTAINER_URL"
exit 1
fi
JWT_TOKEN=$(echo "$AUTH_RESPONSE" | jq -r '.jwt // empty')
if [ -z "$JWT_TOKEN" ] || [ "$JWT_TOKEN" = "null" ]; then
print_error "Authentication failed. Please check your credentials."
print_error "Response: $AUTH_RESPONSE"
exit 1
fi
print_status "Authentication successful."
}
# Function to get all environments (endpoints)
get_environments() {
print_status "Getting environments..."
ENVIRONMENTS_RESPONSE=$(curl -s -X GET \
-H "Authorization: Bearer $JWT_TOKEN" \
"$PORTAINER_URL/api/endpoints")
if [ $? -ne 0 ]; then
print_error "Failed to get environments"
exit 1
fi
echo "$ENVIRONMENTS_RESPONSE" | jq -r '.[].Id' 2>/dev/null || echo "1"
}
# Function to get all stacks
get_stacks() {
print_status "Getting list of stacks..." >&2
STACKS_RESPONSE=$(curl -s -X GET \
-H "Authorization: Bearer $JWT_TOKEN" \
"$PORTAINER_URL/api/stacks")
if [ $? -ne 0 ]; then
print_error "Failed to get stacks" >&2
exit 1
fi
echo "$STACKS_RESPONSE"
}
# Function to get detailed stack information
get_stack_details() {
local stack_id=$1
local endpoint_id=$2
curl -s -X GET \
-H "Authorization: Bearer $JWT_TOKEN" \
"$PORTAINER_URL/api/stacks/$stack_id?endpointId=$endpoint_id"
}
# Function to get stack file content
get_stack_file() {
local stack_id=$1
curl -s -X GET \
-H "Authorization: Bearer $JWT_TOKEN" \
"$PORTAINER_URL/api/stacks/$stack_id/file"
}
# Function to save stack data
save_stack_data() {
local stack_info=$1
local stack_name=$(echo "$stack_info" | jq -r '.Name')
local stack_id=$(echo "$stack_info" | jq -r '.Id')
local endpoint_id=$(echo "$stack_info" | jq -r '.EndpointId')
print_status "Processing stack: $stack_name (ID: $stack_id)"
# Create directory for this stack
local stack_dir="$OUTPUT_DIR/$stack_name"
mkdir -p "$stack_dir"
# Save basic stack information
echo "$stack_info" | jq '.' > "$stack_dir/stack-info.json"
# Extract and save environment variables
echo "$stack_info" | jq '.Env // []' > "$stack_dir/environment-variables.json"
# Save environment variables in .env format
echo "$stack_info" | jq -r '.Env[]? | select(.name != null and .value != null) | "\(.name)=\(.value)"' > "$stack_dir/.env"
# Get and save stack file content (docker-compose.yml)
print_status "Getting stack file for: $stack_name"
STACK_FILE_RESPONSE=$(get_stack_file "$stack_id")
if [ $? -eq 0 ] && [ "$STACK_FILE_RESPONSE" != "null" ]; then
echo "$STACK_FILE_RESPONSE" | jq -r '.StackFileContent // empty' > "$stack_dir/docker-compose.yml"
else
print_warning "Could not retrieve stack file for: $stack_name"
fi
# Save additional metadata
cat > "$stack_dir/metadata.txt" << EOF
Stack Name: $stack_name
Stack ID: $stack_id
Endpoint ID: $endpoint_id
Creation Date: $(echo "$stack_info" | jq -r '.CreationDate // "N/A"')
Update Date: $(echo "$stack_info" | jq -r '.UpdateDate // "N/A"')
Status: $(echo "$stack_info" | jq -r '.Status // "N/A"')
Type: $(echo "$stack_info" | jq -r '.Type // "N/A"')
Entry Point: $(echo "$stack_info" | jq -r '.EntryPoint // "N/A"')
EOF
print_status "Stack $stack_name saved to $stack_dir"
}
# Main execution
main() {
print_status "Starting Portainer stack backup..."
check_dependencies
get_config
authenticate
# Create output directory
mkdir -p "$OUTPUT_DIR"
# Get all stacks
STACKS_JSON=$(get_stacks)
if [ -z "$STACKS_JSON" ] || [ "$STACKS_JSON" = "null" ]; then
print_warning "No stacks found or unable to retrieve stacks."
exit 1
fi
# Count stacks
STACK_COUNT=$(echo "$STACKS_JSON" | jq '. | length')
print_status "Found $STACK_COUNT stack(s)"
if [ "$STACK_COUNT" -eq 0 ]; then
print_warning "No stacks to backup."
exit 0
fi
# Process each stack
echo "$STACKS_JSON" | jq -c '.[]' | while read -r stack; do
save_stack_data "$stack"
echo "---"
done
print_status "Backup completed! All stacks saved to: $OUTPUT_DIR"
print_status "Summary:"
ls -la "$OUTPUT_DIR"
}
# Run main function
main "$@"