GPG Encryption Setup Guide
Complete guide for setting up GPG encryption for secure email backups.
đź’ˇ New Feature: For Docker users, check out the GPG Key Import Guide for a simpler way to use GPG encryption without mounting keyrings. You can now import public keys from files, URLs, or environment variables using
--gpg-import-key
.
Table of Contents
- Why Use GPG Encryption
- Installing GPG
- Creating GPG Keys
- Exporting and Backing Up Keys
- Using GPG with Docker
- Key Management
- Best Practices
Why Use GPG Encryption
GPG (GNU Privacy Guard) encryption provides:
- End-to-end encryption: Files are encrypted before upload to S3
- Security: Only holders of the private key can decrypt
- Compliance: Meets data protection requirements (GDPR, HIPAA)
- Trust: Industry-standard encryption (OpenPGP)
Use Cases
- Storing backups in untrusted locations
- Multi-tenant backup systems
- Regulatory compliance requirements
- Long-term archival with security guarantees
Installing GPG
On Linux
# Debian/Ubuntu
sudo apt update
sudo apt install gnupg
# RHEL/CentOS/Fedora
sudo yum install gnupg
# Arch Linux
sudo pacman -S gnupg
On macOS
# Using Homebrew
brew install gnupg
On Windows
Download from GnuPG website or use Gpg4win
Verify Installation
gpg --version
Creating GPG Keys
Generate a New Key Pair
gpg --full-generate-key
Follow the prompts:
- Key type: Select
(1) RSA and RSA (default)
- Key size: Choose
4096
for maximum security - Expiration: Recommend
0
(never expire) for backup keys - Real name: Your name or
Email Backup Key
- Email:
backup@example.com
or your email - Comment:
Email backup encryption key
- Passphrase: Important: Set a strong passphrase
Example:
$ gpg --full-generate-key
gpg (GnuPG) 2.2.19; Copyright (C) 2019 Free Software Foundation, Inc.
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Real name: Email Backup
Email address: backup@example.com
Comment: Backup encryption key
You selected this USER-ID:
"Email Backup (Backup encryption key) <backup@example.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
Quick Generation for Testing
# Fast key generation for testing (NOT for production)
gpg --batch --generate-key <<EOF
Key-Type: RSA
Key-Length: 2048
Name-Real: Test Backup
Name-Email: test@example.com
Expire-Date: 0
%no-protection
EOF
List Your Keys
# List public keys
gpg --list-keys
# List private keys
gpg --list-secret-keys
Exporting and Backing Up Keys
Export Public Key
# Export to file
gpg --export --armor backup@example.com > backup-public-key.asc
# Display key
gpg --export --armor backup@example.com
Export Private Key
⚠️ IMPORTANT: Keep your private key secure!
# Export private key
gpg --export-secret-keys --armor backup@example.com > backup-private-key.asc
# Secure the file
chmod 600 backup-private-key.asc
Backup Your Keys
#!/bin/bash
# backup-gpg-keys.sh
BACKUP_DIR="$HOME/secure-backup"
KEY_EMAIL="backup@example.com"
DATE=$(date +%Y%m%d)
mkdir -p "$BACKUP_DIR"
# Export public key
gpg --export --armor "$KEY_EMAIL" > "$BACKUP_DIR/public-$DATE.asc"
# Export private key
gpg --export-secret-keys --armor "$KEY_EMAIL" > "$BACKUP_DIR/private-$DATE.asc"
# Export trust database
gpg --export-ownertrust > "$BACKUP_DIR/ownertrust-$DATE.txt"
# Secure the directory
chmod 700 "$BACKUP_DIR"
chmod 600 "$BACKUP_DIR"/*
echo "GPG keys backed up to $BACKUP_DIR"
# Optional: Encrypt the backup
tar czf - "$BACKUP_DIR" | gpg --encrypt --recipient "$KEY_EMAIL" > gpg-backup-$DATE.tar.gz.gpg
Import Keys on Another System
# Import public key
gpg --import backup-public-key.asc
# Import private key
gpg --import backup-private-key.asc
# Import trust
gpg --import-ownertrust < ownertrust.txt
# Verify
gpg --list-secret-keys
Using GPG with Docker
Method 1: Flexible Key Import (Recommended)
No keyring mount needed - Import public keys from files, URLs, or environment variables:
# From URL (great for automation)
docker run --rm \
-v $(pwd)/backups:/data \
user2k20/imapbackup \
-s imap.example.com \
-u user@example.com \
-e \
--s3-upload \
--s3-endpoint=https://s3.hetzner.cloud \
--s3-bucket=encrypted-backups \
--s3-access-key=$S3_KEY \
--s3-secret-key=$S3_SECRET \
--gpg-encrypt \
--gpg-recipient=backup@example.com \
--gpg-import-key=https://example.com/keys/backup-public.asc
# From environment variable
export GPG_PUBLIC_KEY=$(cat ~/keys/backup-public.asc)
docker run --rm \
-v $(pwd)/backups:/data \
-e GPG_PUBLIC_KEY \
user2k20/imapbackup \
-s imap.example.com \
-u user@example.com \
-e \
--gpg-encrypt \
--gpg-recipient=backup@example.com \
--gpg-import-key=env:GPG_PUBLIC_KEY
# From file in Docker image
docker run --rm \
-v $(pwd)/backups:/data \
-v $(pwd)/backup-public.asc:/etc/gpg-key.asc:ro \
user2k20/imapbackup \
-s imap.example.com \
-u user@example.com \
-e \
--gpg-encrypt \
--gpg-recipient=backup@example.com \
--gpg-import-key=/etc/gpg-key.asc
Benefits:
- No GPG keyring management
- Public keys are safe to distribute
- Perfect for containers and automation
- Works with Kubernetes ConfigMaps
See the GPG Key Import Guide for complete examples and workflows.
Method 2: Share GPG Keyring with Container (Traditional)
Mount your GPG directory (useful when you have existing keyrings):
# Mount GPG directory (read-only recommended)
docker run --rm \
-v $(pwd)/backups:/data \
-v ~/.gnupg:/root/.gnupg:ro \
user2k20/imapbackup \
-s imap.example.com \
-u user@example.com \
-e \
--s3-upload \
--s3-endpoint=https://s3.hetzner.cloud \
--s3-bucket=encrypted-backups \
--s3-access-key=$S3_KEY \
--s3-secret-key=$S3_SECRET \
--gpg-encrypt \
--gpg-recipient=backup@example.com
Note: This method requires managing GPG permissions and is needed for restore/decryption (which requires the private key).
Fix GPG Permissions
# GPG requires specific permissions
chmod 700 ~/.gnupg
chmod 600 ~/.gnupg/*
chmod 700 ~/.gnupg/*.d
Test GPG in Docker
# Test that GPG can access your keys
docker run --rm \
-v ~/.gnupg:/root/.gnupg:ro \
alpine:latest sh -c "apk add gnupg && gpg --list-keys"
Encryption and Decryption Examples
Encrypt a File
# Encrypt for specific recipient
gpg --encrypt --recipient backup@example.com file.txt
# Encrypt with armor (ASCII output)
gpg --encrypt --armor --recipient backup@example.com file.txt
# Encrypt for multiple recipients
gpg --encrypt \
--recipient backup@example.com \
--recipient recovery@example.com \
file.txt
Decrypt a File
# Decrypt file
gpg --decrypt file.txt.gpg > file.txt
# Decrypt to specific output
gpg --output file.txt --decrypt file.txt.gpg
# Batch mode (no prompts)
gpg --batch --yes --decrypt file.txt.gpg > file.txt
Test Encryption/Decryption
# Create test file
echo "Test backup data" > test.txt
# Encrypt
gpg --encrypt --recipient backup@example.com test.txt
# Verify encrypted
file test.txt.gpg
# Decrypt
gpg --decrypt test.txt.gpg > test-decrypted.txt
# Compare
diff test.txt test-decrypted.txt
Complete Backup Example with GPG
#!/bin/bash
# encrypted-backup.sh
# Configuration
IMAP_SERVER="imap.example.com"
IMAP_USER="user@example.com"
IMAP_PASS_FILE="/root/.imap_password"
GPG_RECIPIENT="backup@example.com"
S3_ENDPOINT="https://s3.hetzner.cloud"
S3_BUCKET="encrypted-email-backups"
S3_PREFIX="backups/$(date +%Y)/$(date +%m)/"
BACKUP_DIR="/tmp/email-backup"
echo "Starting encrypted backup at $(date)"
# Run backup with encryption
docker run --rm \
-v "$BACKUP_DIR:/data" \
-v ~/.gnupg:/root/.gnupg:ro \
-v "$IMAP_PASS_FILE:/secrets/password:ro" \
user2k20/imapbackup \
-s "$IMAP_SERVER" \
-u "$IMAP_USER" \
-p @/secrets/password \
-e \
--s3-upload \
--s3-endpoint="$S3_ENDPOINT" \
--s3-bucket="$S3_BUCKET" \
--s3-access-key="$S3_ACCESS_KEY" \
--s3-secret-key="$S3_SECRET_KEY" \
--s3-prefix="$S3_PREFIX" \
--gpg-encrypt \
--gpg-recipient="$GPG_RECIPIENT" \
--nospinner
if [ $? -eq 0 ]; then
echo "Backup completed successfully at $(date)"
# Clean up local unencrypted files
rm -rf "$BACKUP_DIR"/*
else
echo "Backup failed at $(date)"
exit 1
fi
Key Management
Trust Keys
# Edit key to set trust level
gpg --edit-key backup@example.com
# In GPG prompt:
gpg> trust
# Select: 5 = I trust ultimately
gpg> quit
Renew Expiring Keys
# Edit key
gpg --edit-key backup@example.com
# Extend expiration
gpg> expire
# Select new expiration date
gpg> save
Revoke a Compromised Key
# Generate revocation certificate
gpg --output revoke.asc --gen-revoke backup@example.com
# Import revocation (when needed)
gpg --import revoke.asc
# Publish to keyserver
gpg --send-keys <KEY_ID>
Multiple Keys for Different Purposes
# Generate separate keys
gpg --full-generate-key # For daily backups
gpg --full-generate-key # For archival backups
gpg --full-generate-key # For disaster recovery
# Use appropriate key
--gpg-recipient=daily-backup@example.com
--gpg-recipient=archive-backup@example.com
--gpg-recipient=dr-backup@example.com
Best Practices
1. Use Strong Passphrases
# Generate strong passphrase
openssl rand -base64 32
# Use password manager to store it
2. Key Rotation
# Create new key annually
gpg --full-generate-key
# Re-encrypt old backups with new key
for file in *.gpg; do
gpg --decrypt "$file" | gpg --encrypt --recipient new-backup@example.com > "new-$file"
done
3. Store Private Keys Securely
- Use hardware security keys (YubiKey, Nitrokey)
- Store offline in secure location
- Use encrypted USB drives
- Multiple backup locations
4. Document Key Information
# Create key documentation
cat > gpg-key-info.txt <<EOF
GPG Backup Key Information
==========================
Key ID: $(gpg --list-keys backup@example.com | grep pub | awk '{print $2}')
Email: backup@example.com
Created: $(date)
Fingerprint: $(gpg --fingerprint backup@example.com | grep fingerprint | awk -F= '{print $2}')
Location: ~/.gnupg and backed up to /secure-storage/gpg-backup/
Passphrase: Stored in password manager under "GPG Backup Key"
Recovery Instructions:
1. Import private key: gpg --import backup-private-key.asc
2. Import trust: gpg --import-ownertrust < ownertrust.txt
3. Test decryption: gpg --decrypt test.txt.gpg
EOF
5. Test Recovery Regularly
#!/bin/bash
# test-gpg-recovery.sh
echo "Testing GPG recovery process..."
# Create test backup
echo "Test data $(date)" > test-backup.txt
gpg --encrypt --recipient backup@example.com test-backup.txt
# Simulate fresh system
TEST_HOME=$(mktemp -d)
export GNUPGHOME="$TEST_HOME"
# Import keys
gpg --import backup-private-key.asc
gpg --import-ownertrust < ownertrust.txt
# Test decryption
gpg --decrypt test-backup.txt.gpg > recovered.txt
# Verify
if diff test-backup.txt recovered.txt; then
echo "âś“ Recovery test PASSED"
rm -rf "$TEST_HOME"
exit 0
else
echo "âś— Recovery test FAILED"
exit 1
fi
6. Monitor Key Expiration
# Check key expiration
gpg --list-keys backup@example.com | grep expires
# Set reminder before expiration
echo "Check GPG key expiration" | at now + 11 months
Troubleshooting
“No secret key” Error
# Verify private key exists
gpg --list-secret-keys
# Import if missing
gpg --import backup-private-key.asc
Permission Denied
# Fix GPG directory permissions
chmod 700 ~/.gnupg
find ~/.gnupg -type f -exec chmod 600 {} \;
find ~/.gnupg -type d -exec chmod 700 {} \;
“Unusable public key”
# Trust the key
gpg --edit-key backup@example.com
gpg> trust
gpg> 5
gpg> quit
Docker Can’t Access GPG
# Verify mount
docker run --rm -v ~/.gnupg:/root/.gnupg:ro alpine ls -la /root/.gnupg
# Check SELinux (if applicable)
chcon -Rt svirt_sandbox_file_t ~/.gnupg
Security Checklist
- Generated strong 4096-bit RSA key
- Set strong passphrase on private key
- Backed up private key to secure location
- Backed up trust database
- Tested encryption and decryption
- Documented key information
- Stored passphrase in password manager
- Created revocation certificate
- Tested recovery procedure
- Set up key expiration monitoring
Next Steps
- S3 Setup - Configure cloud storage
- Backup Guide - Create encrypted backups
- Restore Guide - Restore from encrypted backups