Skip to main content
A secure, automated backup solution for applications hosted on Upsun can be implemented using Duplicity with GPG encryption to back up data to Amazon S3. This approach ensures data integrity, confidentiality, and recoverability. The following guide outlines how to set up such a system.

Why this backup strategy?

Traditional backup methods often fall short in cloud-native environments: they may lack encryption, require manual intervention, or fail to integrate cleanly with immutable infrastructure. An S3-based backup solution using Duplicity and GPG encryption addresses these gaps through the following design choices:

End-to-end encryption:

All backups are encrypted at rest using GPG before leaving the application container. Even if S3 credentials are compromised, data remains protected by a strong passphrase and private key. Private keys and passphrases should never be stored in plaintext on the platform.

Efficient incremental backups

Duplicity uses incremental syncing, meaning only changed files are uploaded after the initial full backup. This reduces bandwidth, storage costs, and backup windows. Critical for large Drupal sites with frequent file uploads.

Cloud-native and portable

By using AWS S3 (a durable, versionable, and globally available object store) and boto3 (the official AWS SDK), avoid vendor lock-in while leveraging battle-tested infrastructure. The same backup can be restored anywhere, like Upsun, local dev, or another cloud.

Declarative and reproducible

Thanks to Upsun’s composable Nix stack, every dependency (Duplicity, GnuPG, Python packages) is versioned and immutable. There’s no “works on my machine” problem. Your backup environment is identical across all deployments.

Fully automated

With cron-driven execution and deploy-time key setup, backups run without human intervention. Combined with environment-based toggles (S3_BACKUP=On), it’s easy to enable/disable per environment (on for prod, off for dev).

How to set up:

1. Configure the composable stack

Upsun’s composable images (based on Nix) let you declaratively define your runtime environment. To support encrypted S3 backups, add the required packages to your
applications:
  drupal:
    type: 'composable:25.05'
    stack:
      - gnupg
      - duplicity
      - python3
      - python3Packages.numpy
      - python3Packages.boto3
      # Other required packages...

Why these packages?

  • Duplicity: Handles incremental, encrypted backups.
  • gnupg: Manages GPG key operations.
  • boto3: Enables Duplicity to communicate with AWS S3.
  • python3 & numpy: Runtime dependencies for Duplicity and boto3.

2. Generate GPG keys locally

Backups are encrypted using GPG. Generate a dedicated key pair on your local machine:
gpg --full-generate-key
  • Choose RSA and RSA (default)
  • Set key size to 4096 bits.
  • Set a strong passphrase (you’ll store this securely)
  • Use a descriptive name/email (devops+upsun-backup@example.com)
After creation, note the Key ID (ABC123DEF456):

Export the keys in ASCII-armored format:

# Public key
gpg --armor --export ABC123DEF456 > public.key

# Private key
gpg --armor --export-secret-keys ABC123DEF456 > private.key
Tip: Store the passphrase, Key ID, and both key files in your secret storage or password manager.

3. Set environment variables

Upsun has a 4096-byte limit on environment variable values. Since GPG private keys often exceed this, split the private key into two parts:
GPG_PASSPHRASE=your_strong_passphrase
GPG_KEY_ID=ABC123DEF456
GPG_PUBLIC_KEY=-----BEGIN PGP PUBLIC KEY BLOCK-----...
GPG_KEY_PART1=-----BEGIN PGP PRIVATE KEY BLOCK-----... (first half)
GPG_KEY_PART2=... (second half)-----END PGP PRIVATE KEY BLOCK-----
AWS_ACCESS_KEY_ID=your_aws_key
AWS_SECRET_ACCESS_KEY=your_aws_secret
AWS_REGION=us-east-2
AWS_DEFAULT_REGION=us-east-2
S3_BACKUP=On
Tip: When adding environment variables or keys in Upsun, especially long ones such as SSH keys or encrypted tokens, it’s generally easier to use the Upsun Console UI rather than the CLI.
The Settings > Variables section in UI allows you to paste or edit long multi-line values directly without worrying about formatting issues or line breaks.

4. Create an S3 bucket

Create a dedicated S3 bucket in your preferred region (us-east-2):
aws s3api create-bucket \
    --bucket your-unique-backup-bucket-name \
    --region us-east-2 \
Security note: The bucket does not need to be publicly accessible. Ensure your AWS credentials have s3:PutObject, s3:GetObject, and s3:ListBucket permissions only. Set the bucket name via the AWS_S3_BUCKET environment variable.

5. Automate backups with a cron job

Add a cron task to .upsun/config.yaml to run incremental backups every 30 minutes:
You can adjust the schedule to match your preference.
crons:
  backup:
    spec: '*/30 * * * *'
    commands:
      start: |
        if [ "$S3_BACKUP" = "On" ]; then
          PASSPHRASE="$GPG_PASSPHRASE" GNUPGHOME=/tmp/gnupg \
          duplicity incremental \
            --encrypt-key "$GPG_KEY_ID" \
            /app \
            boto3+s3://$AWS_S3_BUCKET
        fi
Cron ExpressionMeaning
***/30 * * * ***Every 30 minutes
**0 2 * * ***Daily at 2 AM (for full)
0 1 * * 0Weekly at 1 AM on Sunday

What’s backed up?

By default, the entire /app directory (containing the application codebase and files) is backed up incrementally. Additional directories can be included as needed.

6. Import GPG keys on deploy

During deployment, import your GPG keys into a temporary keyring:
hooks:
  deploy: |
    set -e
    if [ "$S3_BACKUP" = "On" ]; then
      export GNUPGHOME=/tmp/gnupg
      mkdir -p /tmp/gnupg
      chmod 700 /tmp/gnupg

      # Reconduct the keys
      echo "$GPG_PUBLIC_KEY" > /tmp/gnupg/public.key
      echo "$GPG_KEY_PART1$GPG_KEY_PART2" > /tmp/gnupg/private.key
      # Secure the keys
      chmod 600 /tmp/gnupg/public.key
      chmod 600 /tmp/gnupg/private.key
      # Import public key
      gpg --batch --import /tmp/gnupg/public.key
      # Import private key
      gpg --batch --import /tmp/gnupg/private.key
      # Trust the key fully
      echo -e "trust\n5\ny\n" | gpg --command-fd 0 --edit-key "$GPG_KEY_ID"
      # Delete the key files
      rm -f /tmp/gnupg/public.key
      rm -f /tmp/gnupg/private.key
    fi
This ensures Duplicity can encrypt (and later decrypt) backups.

7. Test Your first backup

After deployment, trigger a manual full backup to verify the setup:
# SSH into your environment
upsun ssh

# Run full backup
PASSPHRASE="$GPG_PASSPHRASE" GNUPGHOME=/tmp/gnupg \
duplicity full --encrypt-key "$GPG_KEY_ID" /app boto3+s3://$AWS_S3_BUCKET
Then check your S3 bucket. You should see encrypted .gpg files and a signature file.
Bucket: platform-s3-backup-demo

├── duplicity-full-signatures.20250405T100000Z.sigtar.gpg
├── duplicity-full.20250405T100000Z.manifest.gpg
├── duplicity-full.20250405T100000Z.vol1.difftar.gpg
├── duplicity-inc.20250405T103000Z.to.20250405T110000Z.manifest.gpg
├── duplicity-inc.20250405T103000Z.to.20250405T110000Z.vol1.difftar.gpg
└── ...

8. Restoring from backup

Need to restore? Since Upsun is a read-only environment, use a local or a Cloud Server to restore backups.
  • Spin up a recovery environment on a cloud server or on a local server.
  • Re-import GPG keys
  • Use duplicity restore with your S3 URL and passphrase
  • Sync files back to Upsun

Conclusion

This setup provides secure, automated, and incremental backups of Upsun-hosted applications directly to AWS S3 with end-to-end GPG encryption. By combining Upsun’s composable Nix stack with Duplicity’s robust backup logic, it ensures data resilience while maintaining operational simplicity.
Last modified on April 14, 2026