n8n S3 Binary Data Storage: Complete Tutorial (AWS, R2, Queue Mode, Lifecycle Rules)

Introduction

If your self-hosted n8n instance handles PDFs, images, audio, or CSV exports, every one of those files passes through binary data storage, and the default backend is process memory. A workflow that processes a few hundred attachments can silently grow the Node.js heap until the container is killed, and the execution log tells you almost nothing. This tutorial is for self-hosters on Docker or npm who want file pipelines that survive production volume. You will learn how the four binary data modes compare, how to point n8n at Amazon S3 (or an S3-compatible store like Cloudflare R2), how to keep main and queue-mode workers in sync, and how to set up the lifecycle rule that keeps your bucket from growing forever.

Quick answer

Set N8N_DEFAULT_BINARY_DATA_MODE=s3, fill in the five N8N_EXTERNAL_STORAGE_S3_* variables with your bucket details, and restart n8n. Binary data then streams to S3 instead of RAM, so large files stop crashing your instance.

Evidence: why this matters at scale

Self-hosted automation lives or dies on storage architecture, not on workflow logic. Two numbers frame the decision. First, as of October 2026 the n8n-io/n8n repository has passed 208,000 stars and 61,000 forks on GitHub (checked via the GitHub API on 2026-10-23), which means this is one of the most deployed self-hosted automation platforms in existence, and its community runs production file pipelines every day. Second, AWS documents 99.999999999% (eleven nines) durability for S3 Standard storage classes, so an object bucket is a far safer home for binary payloads than a container’s ephemeral disk or a process heap that vanishes on restart. The n8n binary data environment variable reference documents the four modes (memory, filesystem, database, s3) and the per-mode limits, while the queue mode guide explains why workers need a shared backend. The official external storage documentation states the pricing gate plainly: S3 binary data storage is available on self-hosted Business and Enterprise plans, and it is not available on n8n Cloud. The same page gives the warning that catches most first-time setups: “n8n delegates pruning of binary data to S3, so setting a lifecycle configuration is required unless you want to preserve binary data indefinitely.” If you skip the lifecycle rule, the bucket only grows. For readers planning the whole deployment, our complete n8n self-hosting guide covers installation, scaling, and security in one place.

Prerequisites

  • A self-hosted n8n instance (Docker or npm) on a Business or Enterprise plan with the license key activated. Community edition cannot use S3 mode.
  • n8n version 2.6.4 or newer recommended, because region validation changed in that release (see Troubleshooting).
  • An AWS account with permission to create a bucket and a lifecycle rule, or an S3-compatible provider such as Cloudflare R2 or Backblaze B2.
  • Basic comfort editing a docker-compose file or setting environment variables.
  • If you run queue mode, SSH or console access to every host that runs an n8n main or worker container.

Step-by-step guide

Step 1: Pick the right binary data mode for your workload

Before touching S3, make an explicit choice. n8n offers four modes via N8N_DEFAULT_BINARY_DATA_MODE:

ModeWhere files liveBest forWatch out for
default (memory)Process RAMTiny test workflowsOOM kills on real files; dies with the process
filesystemLocal disk (N8N_USER_FOLDER/binaryData)Single-node DockerDisk fills up; queue-mode workers each see their own disk
databasen8n databaseSmall deploymentsPer-file cap: 512 MiB default, 1024 MiB hard maximum (N8N_BINARY_DATA_DATABASE_MAX_FILE_SIZE); bloats backups
s3Object storageProduction file pipelines, queue modeNeeds Business/Enterprise license; needs a lifecycle rule

Rule of thumb: if your workflows touch files larger than a few tens of megabytes, or if you run queue mode with more than one worker, choose s3. If you keep hitting memory walls even in filesystem mode, read our guide to fix out-of-memory crashes with batch processing alongside this tutorial, since mode choice and batching solve different halves of the problem.

Step 2: Create the bucket and lock it down

Create a dedicated bucket (for example n8n-binary-data) in the AWS console, keeping it private with Block Public Access on. Attach a minimal bucket policy to the IAM user or role n8n will use, replacing <bucket-name> with your bucket name. This policy shape comes from the n8n documentation:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": ["s3:*"],
      "Resource": [
        "arn:aws:s3:::<bucket-name>",
        "arn:aws:s3:::<bucket-name>/*"
      ]
    }
  ]
}

If you run on EKS or EC2, you can skip long-lived keys entirely by giving the instance an IAM role and setting N8N_EXTERNAL_STORAGE_S3_AUTH_AUTO_DETECT=true, which uses the default AWS credential provider chain.

Step 3: Add the S3 lifecycle rule (required, not optional)

n8n does not clean up the bucket for you. Open the bucket in the S3 console, go to Management, Lifecycle rules, and create a rule that expires current object versions after 14 days (match this to your EXECUTIONS_DATA_MAX_AGE, which defaults to 336 hours, also 14 days, so binary data and execution records age out together). The equivalent AWS CLI command:

aws s3api put-bucket-lifecycle-configuration \
  --bucket n8n-binary-data \
  --lifecycle-configuration '{
    "Rules": [{
      "ID": "n8n-binary-prune",
      "Status": "Enabled",
      "Filter": {"Prefix": ""},
      "Expiration": {"Days": 14}
    }]
  }'

Verify it with aws s3api get-bucket-lifecycle-configuration --bucket n8n-binary-data. If this rule is missing, the bucket grows without bound, because n8n delegates binary pruning to S3.

Step 4: Point n8n at the bucket

Add these variables to every n8n container (main and workers, see Step 6). For Docker Compose, put them in an .env file referenced by all n8n services:

N8N_DEFAULT_BINARY_DATA_MODE=s3
N8N_AVAILABLE_BINARY_DATA_MODES=filesystem,s3
N8N_EXTERNAL_STORAGE_S3_HOST=s3.us-east-1.amazonaws.com
N8N_EXTERNAL_STORAGE_S3_BUCKET_NAME=n8n-binary-data
N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION=us-east-1
N8N_EXTERNAL_STORAGE_S3_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE
N8N_EXTERNAL_STORAGE_S3_ACCESS_SECRET=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Keeping filesystem in N8N_AVAILABLE_BINARY_DATA_MODES matters: n8n continues to read binary data that was stored in the filesystem before the switch, instead of erroring on old executions.

For npm installs, export the same variables before starting n8n, or place them in the environment file your process manager reads. Restart n8n, then open any workflow that produces a file (an HTTP Request node downloading a PDF is a good smoke test) and confirm the execution succeeds.

Step 5: The S3-compatible variant (Cloudflare R2, Backblaze B2, MinIO)

n8n officially supports AWS S3; S3-compatible services work but are not officially supported. Two details differ:

  1. Region. If your provider does not require a region, set N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION to the literal string auto. For Cloudflare R2 this is the correct value.
  2. Host and protocol. Set the host to your provider endpoint. For R2 that looks like N8N_EXTERNAL_STORAGE_S3_HOST=<account-id>.r2.cloudflarestorage.com. If your compatible store is served over plain HTTP (for example a local MinIO for testing), also set N8N_EXTERNAL_STORAGE_S3_PROTOCOL=http and include the protocol in the host value, as the n8n docs describe.

Everything else, including the lifecycle rule, behaves the same. R2’s own lifecycle management works identically for expiry.

Step 6: Sync the configuration across queue-mode workers

Queue mode is where S3 pays for itself: workers are separate Node.js processes, often on separate hosts, so they cannot share a local disk. But the configuration must be identical everywhere, or workers silently fail to find files the main process stored. Check these three things:

  1. Identical env vars on main and every worker. The seven N8N_EXTERNAL_STORAGE_S3_* variables plus the two binary-data mode variables must match exactly across docker-compose.yml services. The cleanest way is one shared env_file referenced by the main, worker, and webhook services.
  2. Upgrade everything at once. The n8n docs call this out explicitly: upgrade all n8n components (main, worker, runner) to the same version simultaneously to avoid protocol incompatibilities. A worker on an older version with S3 mode can produce subtle read failures.
  3. Credentials must be reachable from workers. If you use N8N_EXTERNAL_STORAGE_S3_AUTH_AUTO_DETECT=true with an IAM instance profile, every worker host needs the profile too. On multi-host setups, explicit access keys in the shared env file are simpler to reason about; store them in your secrets manager, not in git.

Step 7: Verify the setup end to end

  1. Run a workflow that writes binary data (for example, download a 50 MB test file with an HTTP Request node).
  2. Confirm the object appears in your bucket in the S3 console or with aws s3 ls s3://n8n-binary-data/ --recursive | head.
  3. Check that execution history still opens the file preview in the n8n UI, which proves reads work too.
  4. Watch memory during the run: with S3 mode the container RSS should stay roughly flat, whereas memory mode would climb with file size.

Troubleshooting

n8n fails to start after upgrading to 2.6.4 with S3 connection errors

Cause: Starting from n8n 2.6.4, N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION must contain only alphanumeric characters and hyphens. Underscores and other special characters make n8n fail startup with connection errors, even if the endpoint was reachable and working on older versions. Region-like strings some providers hand out (for example with underscores) now break validation.

Fix: Change the region value to hyphens only (for example us-east-1, or auto for providers like R2 that do not require a region), then redeploy. Check the startup log: if it worked before the upgrade and nothing else changed, the region value is almost certainly the culprit.

n8n refuses to start in s3 mode, complaining about the license

Cause: S3 binary data storage requires an activated Business or Enterprise license on a self-hosted instance. Community edition and unlicensed instances cannot use it.

Fix: Activate your license key first, then enable S3 mode. If you are on Community edition or evaluating, set N8N_DEFAULT_BINARY_DATA_MODE back to filesystem (or database) and revisit after upgrading your plan. Do not leave the variable at s3 on an unlicensed instance; n8n will not start.

Queue-mode workers cannot find binary data that the main process stored

Cause: The worker containers do not have the same S3 configuration as main. Typical variants: the env vars were added only to the main service in docker-compose, the worker runs an older n8n version, or workers lack the IAM role when using credential auto-detection.

Fix: Diff the effective environment of a worker against main (docker exec <worker> env | grep N8N_EXTERNAL_STORAGE). Make the seven S3 variables and both mode variables identical via a shared env file, upgrade main, worker, and runner to the same n8n version in one deploy, and confirm worker hosts can reach the bucket (test with the AWS CLI from a worker host using the same credentials).

The bucket keeps growing even though execution pruning is on

Cause: Execution pruning (EXECUTIONS_DATA_PRUNE) deletes finished executions and their execution data, but for binary data in S3, n8n delegates pruning to the bucket. Without a lifecycle rule, nothing deletes the objects. A related trap: binary data pruning operates on the active mode, so if you stored data in S3 and later switched back to filesystem, n8n only prunes filesystem data and the S3 objects sit forever.

Fix: Add the lifecycle rule from Step 3 if it is missing, with expiry aligned to EXECUTIONS_DATA_MAX_AGE (default 336 hours). If you already have a backlog of orphaned objects, run a one-time manual expiry: create a lifecycle rule with a shorter expiry, let it sweep, then relax it to the normal window.

Old executions show broken file previews after the switch

Cause: N8N_AVAILABLE_BINARY_DATA_MODES no longer lists filesystem, so n8n cannot read binary data stored before the migration.

Fix: Keep N8N_AVAILABLE_BINARY_DATA_MODES=filesystem,s3 (not just s3) until the old executions age out of your retention window. New data goes to S3; reads of historical data keep working. You can drop filesystem from the list later once nothing you care about references it.

FAQ

Is S3 binary data storage available on n8n Cloud? No. It is available on self-hosted n8n with Business or Enterprise plans only, and it is not available on n8n Cloud. Activate your license key before enabling S3 mode; n8n will not start in s3 binary data mode without a valid license.

Can I use Cloudflare R2 or Backblaze B2 instead of AWS S3? Yes, as S3-compatible backends, though n8n does not officially support them. Set the region to auto when the provider does not require one, point the host at the provider endpoint, and use N8N_EXTERNAL_STORAGE_S3_PROTOCOL=http with the protocol in the host value for plain-HTTP stores like local MinIO.

What happens to binary files stored before I switch to S3? n8n keeps reading older binary data from the filesystem as long as filesystem stays in N8N_AVAILABLE_BINARY_DATA_MODES. Only new binary data goes to S3. Remember that pruning operates on the active mode, so keep the S3 lifecycle rule in place for anything the bucket holds.

Do I still need execution data pruning when binary data lives in S3? Yes, they cover different things. Execution pruning deletes finished executions and their execution data on a schedule (by default, executions older than 336 hours or beyond 10,000 total). Binary objects in the bucket are pruned only by the S3 lifecycle rule you configure.

Does S3 mode slow workflows down? Slightly, by one HTTP round trip per file compared to memory, but the difference is invisible next to API calls or model inference. The real performance win is reliability: memory stays flat during file-heavy runs instead of climbing toward an OOM kill. If you are also migrating the database for a larger deployment, our guide to migrate n8n from SQLite to PostgreSQL pairs well with this tutorial.

Conclusion

Moving n8n binary data to S3 trades an invisible memory risk for an explicit, observable storage layer: you get a bucket you can monitor, a lifecycle rule you can audit, and workers that can all reach the same files. The setup takes one bucket, one lifecycle rule, and seven environment variables, with the 2.6.4 region rule and license activation as the two traps to avoid. For the full deployment picture, start from the complete n8n self-hosting guide and layer this storage setup on top.

Frequently asked questions

Is S3 binary data storage available on n8n Cloud?

No. S3 external storage for binary data is available on self-hosted n8n with Business or Enterprise plans only. It is not available on n8n Cloud, where n8n manages storage for you. You must also activate your license key before enabling S3 mode; n8n will not start in s3 binary data mode without a valid license.

Can I use Cloudflare R2 or Backblaze B2 instead of AWS S3?

Yes, as an S3-compatible backend. n8n officially supports AWS S3; S3-compatible services like Cloudflare R2 and Backblaze B2 work but are not officially supported. If your provider does not require a region, set N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION to the literal value 'auto'. For on-premise or S3-compatible storage served over plain HTTP, set N8N_EXTERNAL_STORAGE_S3_PROTOCOL=http and include the protocol in the host value.

What happens to binary files stored before I switch to S3?

n8n keeps reading older binary data from the filesystem as long as filesystem remains listed in N8N_AVAILABLE_BINARY_DATA_MODES. New binary data goes to S3. Note that binary data pruning operates on the active mode: if you later switch back to filesystem, n8n only prunes filesystem data, so plan your S3 lifecycle rule to cover anything the bucket holds.

Do I still need execution data pruning (EXECUTIONS_DATA_PRUNE) when binary data is in S3?

Yes, they are separate concerns. Execution pruning deletes finished executions along with their execution data and binary data on a schedule (default: executions older than 336 hours, or once you exceed 10,000 executions). For the S3 binary data itself, n8n delegates pruning to the bucket, so the S3 lifecycle rule you configure is what actually deletes old objects.

Does storing binary data in S3 make my workflows slower?

There is a small per-file HTTP round trip compared to memory, but it is negligible next to real workflow work like API calls or model inference. For file-heavy pipelines the tradeoff is strongly positive: slightly slower reads beat an out-of-memory crash. In queue mode, S3 doubles as the shared backend every worker can reach, which filesystem mode cannot do without a shared volume.

TP
ToolPilot Team

We install, break, and fix automation tools so you don't have to. Every tutorial on ToolPilot is tested hands-on before publishing.

Keep reading