DEV Community

Pirate Prentice
Pirate Prentice

Posted on

n8n S3 Node: Upload, Download, and Manage Files in AWS S3 (Free Workflow JSON)

AWS S3 is the most widely used object storage service in the world. The n8n S3 node gives you direct access to S3 buckets from inside your automation workflows — upload files, download objects, list bucket contents, delete objects, and copy files without writing a single line of AWS SDK code.

This guide covers credential setup, every supported operation, real-world patterns, and a free workflow JSON you can import today.

What the n8n S3 Node Can Do

Operation What it does
Upload Upload a file or binary data to an S3 bucket
Download Retrieve an object from S3 as binary data
Get All List objects in a bucket (with prefix/delimiter filtering)
Delete Remove an object from a bucket
Copy Copy an object within or between buckets
Pre-signed URL Generate a time-limited URL for direct upload or download

Credential Setup

In n8n, go to Credentials → New → AWS S3 (or AWS credentials if your version uses unified AWS creds).

You need:

  • Access Key ID — from AWS IAM → Users → Security Credentials
  • Secret Access Key — shown only at creation time; store it immediately
  • Region — the AWS region your bucket is in (e.g., us-east-1, eu-west-1)

IAM permissions required (least-privilege policy):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:ListBucket",
        "s3:CopyObject"
      ],
      "Resource": [
        "arn:aws:s3:::your-bucket-name",
        "arn:aws:s3:::your-bucket-name/*"
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Scope the policy to your specific bucket — never use * for S3 resources in production.

Using S3-compatible storage (MinIO, Cloudflare R2, Backblaze B2, DigitalOcean Spaces):
In the credential, set a custom Endpoint URL instead of relying on the default AWS endpoint. Most S3-compatible services work with the same credential format.

Upload a File

The Upload operation takes binary data from a previous node and writes it to S3.

Bucket Name: my-uploads-bucket
File Name: invoices/{{ $json.invoice_id }}.pdf
Binary Property: data
Enter fullscreen mode Exit fullscreen mode

Key tips:

  • Use / in the file name to create a folder structure (S3 uses prefixes, not real folders)
  • The binary property name must match what the previous node outputs (usually data)
  • Set ACL to private unless you specifically need public access

Download a File

Bucket Name: my-uploads-bucket
File Name: invoices/{{ $json.invoice_id }}.pdf
Binary Property: data
Enter fullscreen mode Exit fullscreen mode

The node outputs binary data on the property you specify. Pass it downstream to:

  • Send Email — attach the file
  • HTTP Request — forward to another service
  • Write Binary File — save to disk (self-hosted n8n only)

List Objects in a Bucket

The Get All operation returns a list of objects. Use it to build processing pipelines.

Bucket Name: my-uploads-bucket
Prefix: invoices/2026/
Delimiter: /
Max Keys: 1000
Enter fullscreen mode Exit fullscreen mode
  • Prefix filters to keys starting with that string
  • Delimiter groups keys by folder level
  • Returns up to 1000 objects per call; paginate with Continuation Token for larger buckets

Common Patterns

Form Upload → S3 → Database Record

Capture a file upload from a form, store it in S3, and write the S3 URL to a database:

Webhook (receives multipart form) 
→ S3 Upload (key: uploads/{{ $now.toISO() }}-{{ $json.filename }})
→ Postgres Insert ({ file_url: 's3://my-bucket/' + $json.key, user_id: $json.user_id })
→ Respond to Webhook
Enter fullscreen mode Exit fullscreen mode

Scheduled Backup to S3

Run a nightly backup of a database export and upload it to S3 with a date-stamped key:

Schedule Trigger (daily 02:00)
→ Execute Command (pg_dump mydb > /tmp/backup.sql)
→ Read Binary File (/tmp/backup.sql)
→ S3 Upload (key: backups/{{ $now.format('yyyy-MM-dd') }}/mydb.sql)
→ Slack Notify (backup complete)
Enter fullscreen mode Exit fullscreen mode

Process S3 Files in Batch

List all files in a prefix, loop over them, download each, process, and write results:

Schedule Trigger
→ S3 Get All (prefix: incoming/)
→ Split In Batches (batch size: 5)
→ S3 Download (key: {{ $json.Key }})
→ [process each file]
→ S3 Upload (key: processed/{{ $json.Key }})
→ S3 Delete (key: incoming/{{ $json.Key }})
Enter fullscreen mode Exit fullscreen mode

Pre-signed Upload URL

Generate a time-limited URL so a client can upload directly to S3 without going through your server:

Webhook (client requests upload URL)
→ S3 Pre-signed URL (operation: upload, key: uploads/{{ $json.filename }}, expiry: 300s)
→ Respond to Webhook ({ uploadUrl: {{ $json.url }} })
Enter fullscreen mode Exit fullscreen mode

The client POPs the file directly to S3. Your server never touches the binary data.

Gotchas Table

Symptom Root cause Fix
AccessDenied on upload IAM policy missing s3:PutObject or scoped to wrong bucket Check policy ARN matches exact bucket name
NoSuchBucket error Wrong bucket name or wrong region in credential Verify bucket region matches credential region
Binary data is empty after download Wrong binary property name downstream Check the property name matches what the S3 node outputs
File uploads but shows 0 bytes Previous node didn't output binary data Ensure upstream node sets binary output; use HTTP Request with Response Format: File
Pre-signed URL returns 403 URL expired or bucket policy blocks it Increase expiry; check bucket policy for s3:GetObject permission
S3-compatible storage fails Endpoint URL format wrong Use https:// prefix; omit trailing slash; check provider docs

Free Workflow JSON — File Upload Pipeline

This workflow receives a file via webhook, uploads it to S3, and responds with the S3 key.

{
  "name": "S3 File Upload Pipeline",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "upload-file",
        "responseMode": "responseNode",
        "options": { "binaryData": true }
      },
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [250, 300]
    },
    {
      "parameters": {
        "operation": "upload",
        "bucketName": "={{ $env.S3_BUCKET }}",
        "fileName": "=uploads/{{ $now.toISO() }}-{{ $binary.data.fileName }}",
        "binaryPropertyName": "data"
      },
      "name": "S3 Upload",
      "type": "n8n-nodes-base.s3",
      "typeVersion": 1,
      "position": [480, 300]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ key: $json.key, url: 'https://' + $env.S3_BUCKET + '.s3.amazonaws.com/' + $json.key }) }}"
      },
      "name": "Respond",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [710, 300]
    }
  ],
  "connections": {
    "Webhook": { "main": [[{ "node": "S3 Upload", "type": "main", "index": 0 }]] },
    "S3 Upload": { "main": [[{ "node": "Respond", "type": "main", "index": 0 }]] }
  }
}
Enter fullscreen mode Exit fullscreen mode

Drop a comment below and I'll share the full workflow JSON including the scheduled backup pattern. Tell me which S3 provider you're using (AWS, Cloudflare R2, MinIO, or DigitalOcean Spaces) and I'll include the right endpoint config.

Workflow Starter Pack ($29)

If you want pre-built, production-ready workflows with error handling, retry logic, and monitoring built in, the n8n Workflow Starter Pack includes a complete S3 file pipeline alongside other production workflows.

Every workflow is documented, tested, and imports in under 2 minutes.


What do you use S3 for in your n8n workflows — file uploads, backups, batch processing, or pre-signed URLs? Let me know in the comments.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.