Skip to the content.

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

GPG (GNU Privacy Guard) encryption provides:

Use Cases

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:

  1. Key type: Select (1) RSA and RSA (default)
  2. Key size: Choose 4096 for maximum security
  3. Expiration: Recommend 0 (never expire) for backup keys
  4. Real name: Your name or Email Backup Key
  5. Email: backup@example.com or your email
  6. Comment: Email backup encryption key
  7. 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

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:

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

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

Next Steps