Real-world examples and use cases for Harpoon (hpn).
# Create image list
cat > web-images.txt << EOF
nginx:1.21-alpine
redis:7-alpine
postgres:15-alpine
EOF
# Pull images
hpn pull -f web-images.txt
# Save to ./images/ directory
hpn save -f web-images.txt --path ./images# Load images from ./images/
hpn load --path ./images
# Push to private registry
hpn push -f web-images.txt --registry harbor.company.com --project production --project production# Create config file
mkdir -p ~/.hpn
cat > ~/.hpn/config.yaml << EOF
registry: harbor.company.com
project: production
paths:
save_path: ./images
load_path: ./images
EOF
# Use with default settings
hpn pull -f web-images.txt
hpn save -f web-images.txt
hpn push -f web-images.txtBy default, hpn load verifies tar files against their .sha256 checksum files when present. Use --skip-verify to bypass verification (e.g. for tar files saved without checksums).
hpn load --path ./images
hpn load --path ./images --skip-verifyPass extra flags to the underlying runtime by placing them after --. You can also set runtime.extra_args in config.
# Insecure registry or self-signed cert (e.g. podman/skopeo)
hpn pull -f images.txt -- --tls-verify=false
hpn push -f images.txt --registry harbor.company.com -- --tls-verify=falseInterpretation of these args is up to the underlying tool (docker/podman/nerdctl/skopeo).
Push uses the same retry and timeout configuration as pull (runtime.retry, runtime.timeout in config) for resilience against network failures.
Extract image references from a Helm chart (remote or local) and use the list with pull/save/push. Requires Helm CLI to be installed.
# From a remote chart (repo/name + version)
hpn extract --chart bitnami/nginx --version 15.0.0 -o images.txt
hpn pull -f images.txt
hpn save -f images.txt --path ./images
# From a local chart directory or .tgz
hpn extract --chart ./mychart -o images.txt
hpn extract --chart ./mychart-1.0.0.tgz -f values-prod.yaml -o images.txt
# With custom values files (passed to helm template)
hpn extract --chart bitnami/nginx --version 15.0.0 -f values.yaml -f prod.yaml -o images.txt
# Write to stdout and pipe to pull (use -f - for stdin)
hpn extract --chart ./mychart -o - | hpn pull -f -
hpn extract --chart bitnami/nginx --version 15.0.0 -o - | hpn save -f - --path ./imagesOutput is one image per line, compatible with hpn pull -f, hpn save -f, hpn push -f, and hpn rmi -f. Use -f - to read the image list from stdin.
# Development images
cat > dev-images.txt << EOF
node:18-alpine
python:3.11-alpine
golang:1.21-alpine
mysql:8.0
redis:7-alpine
EOF
# Pull development images
hpn pull -f dev-images.txt
# Save for offline development
hpn save -f dev-images.txt --path ./images# On machine without internet
hpn load --path ./images
# Verify images are available
docker images | grep -E "(node|python|golang|mysql|redis)"# Production images
cat > prod-images.txt << EOF
nginx:1.21-alpine
app:v1.2.3
database:v2.1.0
cache:v1.0.5
EOF
# Staging images
cat > staging-images.txt << EOF
nginx:1.21-alpine
app:v1.2.3-rc1
database:v2.1.0-beta
cache:v1.0.5
EOF
# Deploy to production
hpn push -f prod-images.txt --registry harbor.company.com --project production --project production
# Deploy to staging
hpn push -f staging-images.txt --registry harbor.company.com --project staging --project production# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup hpn
run: |
curl -L https://github.com/your-org/harpoon/releases/latest/download/hpn-linux-amd64 -o hpn
chmod +x hpn
sudo mv hpn /usr/local/bin/
- name: Login to registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login harbor.company.com -u "${{ secrets.REGISTRY_USER }}" --password-stdin
- name: Deploy images
run: |
hpn --auto-fallback push -f production-images.txt --registry harbor.company.com --project production --project production# .github/workflows/multi-stage.yml
name: Multi-stage Deploy
on:
push:
branches: [main, develop]
jobs:
deploy-staging:
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
steps:
- name: Deploy to staging
run: |
hpn push -f images.txt --registry harbor.company.com --project staging --project production
deploy-production:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Deploy to production
run: |
hpn push -f images.txt --registry harbor.company.com --project production --project productionpipeline {
agent any
environment {
REGISTRY = 'harbor.company.com'
PROJECT = 'production'
}
stages {
stage('Pull Images') {
steps {
sh 'hpn pull -f production-images.txt'
}
}
stage('Save Images') {
steps {
sh 'hpn save -f production-images.txt --path ./images'
archiveArtifacts artifacts: 'images/*.tar', fingerprint: true
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
withCredentials([usernamePassword(credentialsId: 'harbor-creds', usernameVariable: 'USER', passwordVariable: 'PASS')]) {
sh 'echo $PASS | docker login $REGISTRY -u $USER --password-stdin'
sh 'hpn push -f production-images.txt --registry $REGISTRY --project $PROJECT --project production'
}
}
}
}
}# Ensure Docker is used
hpn --runtime docker pull -f images.txt
# With custom Docker configuration
export DOCKER_HOST=tcp://docker.company.com:2376
hpn --runtime docker pull -f images.txt# Use Podman (rootless)
hpn --runtime podman pull -f images.txt
# With Podman socket
systemctl --user start podman.socket
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
hpn --runtime podman pull -f images.txt# Auto-detect and fallback
hpn --auto-fallback pull -f images.txt
# With configuration
cat > ~/.hpn/config.yaml << EOF
runtime:
preferred: docker
auto_fallback: true
EOF
hpn pull -f images.txt# Source images from Docker Hub
cat > dockerhub-images.txt << EOF
library/nginx:latest
library/redis:latest
library/postgres:latest
EOF
# Pull from Docker Hub
hpn pull -f dockerhub-images.txt
# Push to private registry (preserves original project structure)
hpn push -f dockerhub-images.txt --registry harbor.company.com --project production# Migration script
#!/bin/bash
set -e
SOURCE_REGISTRY="old-registry.com"
TARGET_REGISTRY="new-registry.com"
PROJECT="migrated"
# Create image list with source registry
sed "s|^|${SOURCE_REGISTRY}/|" base-images.txt > source-images.txt
# Pull from source
hpn pull -f source-images.txt
# Create target image list
sed "s|${SOURCE_REGISTRY}/|${TARGET_REGISTRY}/${PROJECT}/|" source-images.txt > target-images.txt
# Push to target
hpn push -f target-images.txt --registry $TARGET_REGISTRY --project $PROJECT --project production# Extract images from Kubernetes manifests
kubectl get pods -o jsonpath='{.items[*].spec.containers[*].image}' | tr ' ' '\n' | sort -u > k8s-images.txt
# Pre-pull images on nodes
hpn pull -f k8s-images.txt# Extract images from Helm chart
helm template my-app ./chart | grep -oP 'image:\s*\K[^"]*' | sort -u > helm-images.txt
# Pull and save for air-gapped deployment
hpn pull -f helm-images.txt
hpn save -f helm-images.txt --path ./images# On internet-connected machine
cat > airgap-images.txt << EOF
nginx:1.21-alpine
postgres:15-alpine
redis:7-alpine
app:v1.0.0
EOF
# Pull and save
hpn pull -f airgap-images.txt
hpn save -f airgap-images.txt --path ./images
# Create archive
tar -czf airgap-images.tar.gz images/# On air-gapped machine
tar -xzf airgap-images.tar.gz
# Load images
hpn load --path ./images
# Push to local registry
hpn push -f airgap-images.txt --registry localhost:5000 --project apps --project production#!/bin/bash
# batch-process.sh
set -e
REGISTRIES=("harbor.company.com" "registry.company.com")
PROJECTS=("production" "staging" "development")
IMAGE_LISTS=("web-images.txt" "api-images.txt" "db-images.txt")
for registry in "${REGISTRIES[@]}"; do
for project in "${PROJECTS[@]}"; do
for image_list in "${IMAGE_LISTS[@]}"; do
echo "Processing $image_list for $registry/$project"
# Pull images
hpn pull -f "$image_list"
# Push to registry/project
hpn push -f "$image_list" --registry "$registry" --project "$project" --project production
echo "Completed $image_list for $registry/$project"
done
done
done#!/bin/bash
# monitor-images.sh
IMAGE_LIST="critical-images.txt"
REGISTRY="harbor.company.com"
PROJECT="production"
# Check if images exist in registry
while IFS= read --registry image; do
if hpn pull -f <(echo "$image") 2>/dev/null; then
echo "✓ $image is available"
else
echo "✗ $image is missing - pulling and pushing"
# Pull from source
docker pull "$image"
# Push to registry
echo "$image" | hpn push -f - --registry "$REGISTRY" --project "$PROJECT" --project production
fi
done < "$IMAGE_LIST"#!/bin/bash
# cleanup-old-images.sh
# Remove images older than 30 days
docker images --format "table {{.Repository}}:{{.Tag}}\t{{.CreatedAt}}" | \
awk '$2 < "'$(date -d '30 days ago' '+%Y-%m-%d')'" {print $1}' | \
while read image; do
echo "Removing old image: $image"
docker rmi "$image" 2>/dev/null || true
done
# Clean up tar files older than 7 days
find ./images -name "*.tar" -mtime +7 -delete# High-performance configuration
cat > ~/.hpn/config.yaml << EOF
parallel:
max_workers: 8
auto_adjust: true
runtime:
timeout: 15m
retry:
max_attempts: 5
delay: 2s
max_delay: 60s
EOF
# Process large image list
hpn pull -f large-image-list.txt# Use local registry mirror
cat > ~/.hpn/config.yaml << EOF
registry: registry-mirror.company.com
proxy:
enabled: false # Disable if using local mirror
EOF
# Batch operations
hpn pull -f batch1.txt
hpn pull -f batch2.txt
hpn pull -f batch3.txt# Enable debug logging
export HPN_LOG_LEVEL=debug
hpn pull -f images.txt 2>&1 | tee debug.log
# Analyze debug output
grep -i error debug.log
grep -i timeout debug.log# Test different runtimes
hpn --runtime docker pull -f test-image.txt
hpn --runtime podman pull -f test-image.txt
# Check runtime availability
docker version
podman version
nerdctl version# Test with proxy
export http_proxy=http://proxy:8080
export https_proxy=http://proxy:8080
hpn pull -f images.txt
# Test without proxy
unset http_proxy https_proxy
hpn pull -f images.txt- User Guide - Complete usage guide
- Configuration - Configuration reference
- Quick Start - Getting started guide
- Troubleshooting - Common issues and solutions