Skip to main content
Documenso supports two storage backends for document files:
  • Database - Store files directly in PostgreSQL (default)
  • S3 - Store files in Amazon S3 or S3-compatible services

Storage Comparison

FeatureDatabaseS3
Setup ComplexitySimpleModerate
Best ForSmall deployments, getting startedProduction, large files, scalability
CostDatabase storage costsS3 storage + API costs
ScalabilityLimited by database sizeUnlimited
BackupIncluded in database backupSeparate backup needed
CDN SupportNoYes (CloudFront, etc.)

Database Storage (Default)

Files are stored as binary data (bytea) in the PostgreSQL database.

Configuration

NEXT_PUBLIC_UPLOAD_TRANSPORT="database"

Pros

  • Simple setup - No additional services required
  • ACID guarantees - Files and metadata always in sync
  • Single backup - Database backup includes files
  • No external dependencies

Cons

  • Database bloat - Large files increase database size
  • Performance - Can slow down database queries
  • Scalability - Limited by database storage capacity
  • Cost - Database storage typically more expensive than object storage
  • Development and testing
  • Small deployments (<1000 documents)
  • Simplified backup requirements
  • When S3 costs/complexity are not justified

S3 Storage

Files are stored in Amazon S3 or S3-compatible object storage (MinIO, Wasabi, Cloudflare R2, DigitalOcean Spaces, etc.).

Configuration

NEXT_PUBLIC_UPLOAD_TRANSPORT
string
required
Set to "s3" to enable S3 storage.
NEXT_PUBLIC_UPLOAD_TRANSPORT="s3"
NEXT_PRIVATE_UPLOAD_BUCKET
string
required
Name of the S3 bucket to store documents.
NEXT_PRIVATE_UPLOAD_BUCKET="documenso-documents"
NEXT_PRIVATE_UPLOAD_REGION
string
default:"us-east-1"
AWS region where the bucket is located.
NEXT_PRIVATE_UPLOAD_REGION="us-east-1"
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID
string
AWS access key ID or S3-compatible access key.
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY
string
AWS secret access key or S3-compatible secret key.
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
NEXT_PRIVATE_UPLOAD_ENDPOINT
string
Custom S3 endpoint for S3-compatible providers (MinIO, Wasabi, etc.).Not needed for AWS S3.
NEXT_PRIVATE_UPLOAD_ENDPOINT="https://s3.wasabisys.com"
NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE
string
default:"false"
Use path-style URLs instead of virtual-hosted-style. Required for some S3-compatible providers.
  • false (default): https://bucket.domain.com/path
  • true: https://domain.com/bucket/path
NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE="true"

CloudFront/CDN Support (Optional)

NEXT_PRIVATE_UPLOAD_DISTRIBUTION_DOMAIN
string
CloudFront distribution domain for serving files.
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_DOMAIN="https://d1234567890.cloudfront.net"
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_ID
string
CloudFront key pair ID for signing URLs.
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_ID="APKAEXAMPLE"
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_CONTENTS
string
Private key for CloudFront signed URLs.
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_CONTENTS="-----BEGIN PRIVATE KEY-----\n..."

Provider-Specific Setup

1. Create S3 Bucket

aws s3 mb s3://documenso-documents --region us-east-1

2. Configure Bucket Policy

The bucket should be private. Documenso uses pre-signed URLs for access.
bucket-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyPublicAccess",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::documenso-documents/*",
      "Condition": {
        "StringNotEquals": {
          "aws:PrincipalArn": "arn:aws:iam::ACCOUNT_ID:user/documenso"
        }
      }
    }
  ]
}

3. Create IAM User

aws iam create-user --user-name documenso

4. Attach Policy

documenso-s3-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::documenso-documents/*"
    }
  ]
}
aws iam put-user-policy --user-name documenso \
  --policy-name S3Access \
  --policy-document file://documenso-s3-policy.json

5. Create Access Keys

aws iam create-access-key --user-name documenso

6. Configure Documenso

.env
NEXT_PUBLIC_UPLOAD_TRANSPORT="s3"
NEXT_PRIVATE_UPLOAD_BUCKET="documenso-documents"
NEXT_PRIVATE_UPLOAD_REGION="us-east-1"
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

Testing Storage Configuration

After configuring storage, test it by uploading a document:
  1. Start Documenso
  2. Create a new document
  3. Check the logs for any S3 errors
  4. Verify the file appears in your S3 bucket

Migrating Between Storage Backends

Changing storage backends requires migrating existing files. This is a manual process.

From Database to S3

  1. Set up S3 storage configuration
  2. Create a migration script to:
    • Query all document data from the database
    • Upload files to S3
    • Update database records with S3 keys
  3. Switch NEXT_PUBLIC_UPLOAD_TRANSPORT to s3
  4. Restart Documenso
A migration tool may be added to Documenso in the future. For now, contact support if you need assistance migrating.

Security Best Practices

1

Use Private Buckets

Never make your S3 bucket publicly accessible. Documenso uses pre-signed URLs for secure access.
2

Enable Versioning

Enable bucket versioning to protect against accidental deletion:
aws s3api put-bucket-versioning \
  --bucket documenso-documents \
  --versioning-configuration Status=Enabled
3

Enable Encryption

Enable server-side encryption:
aws s3api put-bucket-encryption \
  --bucket documenso-documents \
  --server-side-encryption-configuration '{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "AES256"
      }
    }]
  }'
4

Set Up Lifecycle Rules

Configure lifecycle policies to manage old versions and reduce costs:
lifecycle.json
{
  "Rules": [
    {
      "Id": "DeleteOldVersions",
      "Status": "Enabled",
      "NoncurrentVersionExpiration": {
        "NoncurrentDays": 90
      }
    }
  ]
}
aws s3api put-bucket-lifecycle-configuration \
  --bucket documenso-documents \
  --lifecycle-configuration file://lifecycle.json
5

Monitor Access

Enable S3 access logging or CloudTrail to audit file access.

Troubleshooting

”Invalid upload transport” error

Cause: NEXT_PUBLIC_UPLOAD_TRANSPORT is not set to s3 when trying to use S3 storage. Solution: Set NEXT_PUBLIC_UPLOAD_TRANSPORT="s3".

”Access Denied” errors

Cause: Insufficient IAM permissions or incorrect credentials. Solution: Verify the IAM user/role has s3:PutObject, s3:GetObject, and s3:DeleteObject permissions.

”Bucket not found” error

Cause: Bucket doesn’t exist or wrong region. Solution: Create the bucket in the specified region:
aws s3 mb s3://documenso-documents --region us-east-1

Connection timeout with MinIO/custom endpoint

Cause: Incorrect endpoint URL or network issues. Solution:
  • Verify the endpoint is accessible from your Documenso instance
  • Check NEXT_PRIVATE_UPLOAD_ENDPOINT is correct
  • Set NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE="true" for MinIO

Performance Optimization

Use CloudFront for Downloads

Configure CloudFront to cache document downloads:
  1. Create a CloudFront distribution with your S3 bucket as origin
  2. Configure signed URLs for security
  3. Set appropriate cache TTLs
  4. Configure Documenso with CloudFront variables

Regional Deployment

For global deployments:
  • Create S3 buckets in multiple regions
  • Use geo-routing to direct users to the nearest bucket
  • Consider S3 Cross-Region Replication for redundancy

Next Steps

Email Configuration

Set up email delivery

Database Configuration

Optimize database settings