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/*"
]
}
]
}
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
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
privateunless you specifically need public access
Download a File
Bucket Name: my-uploads-bucket
File Name: invoices/{{ $json.invoice_id }}.pdf
Binary Property: data
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
- 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
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)
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 }})
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 }} })
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 }]] }
}
}
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.