Skip to content

Setup Document Encryption

Document encryption in Papra provides end-to-end protection for your stored documents using industry-standard AES-256-GCM encryption. This guide will walk you through enabling encryption, understanding how it works, and managing encryption keys.

Papra uses a two-layer encryption approach that provides both security and flexibility:

  1. Key Encryption Key (KEK): A master key that you provide, used to encrypt document-specific keys
  2. Document Encryption Key (DEK): Unique per-document keys that actually encrypt your files
  3. File Encryption: Each document gets its own random 256-bit encryption key for maximum security

Key Encryption Architecture

  1. Document Upload: When you upload a document, Papra generates a unique 256-bit encryption key (DEK)

  2. File Encryption: The document is encrypted using AES-256-GCM with the DEK

  3. Key Wrapping: The DEK is encrypted (wrapped) using your Key Encryption Key (KEK)

  4. Storage: The encrypted document and wrapped DEK are stored separately - the file in your storage backend, the wrapped key in the database along with the document metadata

  5. Retrieval: When accessing a document, Papra unwraps the DEK using your KEK, then decrypts the file stream

  1. Generate an encryption key

    Generate a secure random 256-bit key in hex format, using this generator or OpenSSL command.

    Generated locally in your browser - no network or server involved
  2. Enable encryption in your configuration

    Add the following environment variables to your .env file or Docker configuration:

    Terminal window
    DOCUMENT_STORAGE_ENCRYPTION_IS_ENABLED=true
    DOCUMENT_STORAGE_DOCUMENT_KEY_ENCRYPTION_KEYS=<your-encryption-key>
  3. Restart Papra

    Restart your Papra instance to apply the encryption settings.

VariableDescriptionRequired
DOCUMENT_STORAGE_ENCRYPTION_IS_ENABLEDEnable/disable document encryptionNo
DOCUMENT_STORAGE_DOCUMENT_KEY_ENCRYPTION_KEYSKey encryption keys for document encryptionYes (if encryption enabled)

For simple setups, provide a single 32-byte hex string:

Terminal window
DOCUMENT_STORAGE_DOCUMENT_KEY_ENCRYPTION_KEYS=<your-encryption-key>

This key will automatically be assigned version 1.

Add encryption configuration to your Docker Compose file:

docker-compose.yml
services:
papra:
container_name: papra
image: ghcr.io/papra-hq/papra:latest
restart: unless-stopped
environment:
# ... other environment variables ...
- DOCUMENT_STORAGE_ENCRYPTION_IS_ENABLED=true
- DOCUMENT_STORAGE_DOCUMENT_KEY_ENCRYPTION_KEYS=<your-encryption-key>
volumes:
- ./app-data:/app/app-data
ports:
- "1221:1221"

Key rotation allows you to replace encryption keys without losing access to existing documents:

  1. Generate a new key

    Terminal window
    openssl rand -hex 32
  2. Add the new key with a higher version

    Terminal window
    DOCUMENT_STORAGE_DOCUMENT_KEY_ENCRYPTION_KEYS=1:old_key_here,2:new_key_here
  3. Restart Papra

    New documents will use the highest version key (version 2), while existing documents remain accessible with the old key.

  4. Optional: Remove old keys

    Once you’re confident all documents are using the new key, you can remove old keys. However, this will make any documents encrypted with old keys inaccessible.

  1. Store keys securely: Use a secrets management system in production
  2. Use different keys per environment: Development, staging, and production should have separate keys
  3. Backup your keys: Loss of encryption keys means permanent loss of document access
  4. Rotate keys periodically: Consider rotating keys annually or after security incidents
  5. Limit key access: Only authorized personnel should have access to encryption keys

For production environments, store your encryption keys securely using external secret management systems or secure file systems, and reference them via environment variables.

When you enable encryption on a Papra instance that already has documents:

  • Existing documents: Remain unencrypted but accessible
  • New documents: Are encrypted using the current KEK
  • Mixed storage: Papra automatically handles both encrypted and unencrypted documents

Migrating Existing Documents to Encrypted Format

Section titled “Migrating Existing Documents to Encrypted Format”

If you want to encrypt all existing unencrypted documents after enabling encryption, Papra provides a maintenance command to handle this migration automatically.

  1. Verify encryption is properly configured

    Ensure encryption is enabled and working for new documents before migrating existing ones:

    Terminal window
    # Check that your configuration includes:
    DOCUMENT_STORAGE_ENCRYPTION_IS_ENABLED=true
    DOCUMENT_STORAGE_DOCUMENT_KEY_ENCRYPTION_KEYS=<your-key>
  2. Run dry-run to preview changes

    Terminal window
    # Run dry-run inside the Docker container
    docker compose exec papra pnpm maintenance:encrypt-all-documents --dry-run

    This will show you:

    • How many documents will be encrypted
    • Which documents will be affected
    • No actual encryption will be performed
  3. Run the migration

    Terminal window
    # Run migration inside the Docker container
    docker compose exec papra pnpm maintenance:encrypt-all-documents

    The command will:

    • Find all unencrypted documents
    • Encrypt each document using your configured KEK
    • Update database records with encryption metadata
    • Remove original unencrypted files from storage
    • Provide progress logging throughout the process
  4. Verify migration success

    After migration:

    • Test document access through the Papra interface
    • Check that storage files are now encrypted (should start with PP01)
    • Verify all documents are accessible and downloadable

Migration fails with “Document encryption is not enabled”

  • Verify DOCUMENT_STORAGE_ENCRYPTION_IS_ENABLED=true is set
  • Restart Papra after configuration changes

Migration fails with “Document encryption keys are not set”

  • Ensure DOCUMENT_STORAGE_DOCUMENT_KEY_ENCRYPTION_KEYS contains valid keys
  • Verify key format is correct (64-character hex string)

Migration stops or fails partway

  • Check available disk space
  • Review Papra logs for specific error messages
  • Restore from backup and retry after fixing the issue

Documents inaccessible after migration

  • Verify encryption keys are still properly configured
  • Check that Papra can access your storage backend
  • Restore from backup if necessary

If you disable encryption:

  • Encrypted documents: Remain encrypted but are automatically decrypted when accessed (if KEK is still available)
  • New documents: Are stored unencrypted
  • Data loss risk: If you remove the KEK while encrypted documents exist, those documents become inaccessible

The encryption layer sits between Papra and your chosen storage driver, providing consistent encryption regardless of where files are stored (S3, Azure Blob Storage, File System, etc.).

  • Algorithm: AES-256-GCM (Authenticated Encryption)
  • Key size: 256 bits (32 bytes)
  • IV size: 96 bits (12 bytes)
  • Authentication tag: 128 bits (16 bytes)

Encrypted files use a custom format with a magic number for identification:

| Magic (4 bytes) | IV (12 bytes) | Encrypted Data | Auth Tag (16 bytes) |
  • Magic number: PP01 - identifies Papra encrypted files
  • IV: Initialization vector for GCM mode
  • Encrypted Data: The actual encrypted document content
  • Auth Tag: Authentication tag for integrity verification
  • Streaming encryption: Files are encrypted/decrypted in streams, minimizing memory usage
  • No size overhead: Minimal storage overhead (32 bytes per file for headers)
  • CPU impact: Modern processors handle AES encryption efficiently

“Document KEK required” error

  • Ensure DOCUMENT_STORAGE_DOCUMENT_KEY_ENCRYPTION_KEYS is set
  • Verify the key format is correct (64 character hex string)

“Document KEK not found” error

  • The document was encrypted with a key version that’s no longer available
  • Add the missing key version back to your configuration

“Unsupported encryption algorithm” error

  • The document uses an encryption algorithm not supported by this Papra version
  • This shouldn’t occur in normal operation

Performance issues

  • Consider your storage driver’s performance characteristics
  • Encryption adds minimal overhead, but network/disk I/O remains the bottleneck

To verify encryption is working:

  1. Upload a document after enabling encryption
  2. Check your storage backend - the file should not be readable as plain text
  3. The file should start with the magic number PP01 if you examine it directly

Document encryption in Papra protects against:

  • Storage compromise: If your file storage is breached, documents remain encrypted
  • Database-only breach: Without the KEK, wrapped DEKs cannot be unwrapped
  • Configuration exposure: If the KEK is exposed, the files remain encrypted as long as the DEK are not exposed

Encryption does not protect against:

  • Application-level access: Users with document access can view decrypted content
  • Memory dumps: Decrypted content exists temporarily in application memory
  • Key and database compromise: If KEKs are stolen, all DEKs can be decrypted if the database is compromised
  • Full system compromise: If the entire Papra instance is compromised, documents can be accessed