docs/docs/guides/how-to/s3-asset-storage/index.mdx
This guide demonstrates how to integrate S3-compatible asset storage into your Vendure application using multiple cloud storage platforms. You'll learn to configure a single, platform-agnostic storage solution that works seamlessly with AWS S3, DigitalOcean Spaces, MinIO, CloudFlare R2, and Supabase Storage.
:::info This guide is based on the s3-file-storage example. Refer to the complete working code for full implementation details. :::
Configure your chosen storage provider by following the setup instructions for your preferred platform:
<Tabs> <TabItem value="aws-s3" label="AWS S3">Create an S3 Bucket
my-vendure-assets)Create IAM User with S3 Permissions
Set PermissionsAttach existing policies directly optionAmazonS3FullAccess policy (or create a custom policy with minimal permissions)Generate Access Keys
Environment Variables
# AWS S3 Configuration
S3_BUCKET=my-vendure-assets
S3_ACCESS_KEY_ID=AKIA...
S3_SECRET_ACCESS_KEY=wJalrXUtn...
S3_REGION=us-east-1
# Leave S3_ENDPOINT empty for AWS S3
# Leave S3_FORCE_PATH_STYLE empty for AWS S3
Create Supabase Project
Navigate to Storage
assets (or your preferred name)Generate Service Role Key
Environment Variables
# Supabase Storage Configuration
S3_BUCKET=assets
S3_ACCESS_KEY_ID=your-supabase-access-key-id
S3_SECRET_ACCESS_KEY=your-service-role-key
S3_REGION=us-east-1
S3_ENDPOINT=https://your-project-ref.supabase.co/storage/v1/s3
S3_FORCE_PATH_STYLE=true
:::info
Replace your-project-ref with your actual Supabase project reference ID found in your project settings.
:::
Create a DigitalOcean Account
Create a Space
fra1 for Frankfurt)my-vendure-assets)Generate Spaces Access Keys
Configure CORS Policy (Optional) For browser-based uploads, configure CORS in your Space settings:
[
{
"allowed_origins": ["https://yourdomain.com"],
"allowed_methods": ["GET", "POST", "PUT"],
"allowed_headers": ["*"],
"max_age": 3000
}
]
Environment Variables
# DigitalOcean Spaces Configuration
S3_BUCKET=my-vendure-assets
S3_ACCESS_KEY_ID=DO00...
S3_SECRET_ACCESS_KEY=wJalrXUtn...
S3_REGION=fra1
S3_ENDPOINT=https://fra1.digitaloceanspaces.com
S3_FORCE_PATH_STYLE=false
:::tip
Use the regional endpoint (e.g., https://fra1.digitaloceanspaces.com) not the CDN endpoint. The AWS SDK constructs URLs automatically.
:::
Create CloudFlare Account
Enable R2 Object Storage
Create R2 Bucket
vendure-assetsGenerate API Tokens
Retrieve Credentials
Environment Variables
# CloudFlare R2 Configuration
S3_BUCKET=vendure-assets
S3_ACCESS_KEY_ID=your-r2-access-key
S3_SECRET_ACCESS_KEY=your-r2-secret-key
S3_REGION=auto
S3_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
S3_FORCE_PATH_STYLE=true
:::warning
Replace your-account-id with your actual CloudFlare account ID. If using a custom domain, update S3_FILE_URL to point to your custom domain with https://.
:::
Create Hetzner Cloud Account
Access Object Storage Service
Create Storage Bucket
vendure-assets-yourname)fsn1 for Falkenstein, Germany)Generate S3 API Credentials
Environment Variables
# Hetzner Object Storage Configuration
S3_BUCKET=vendure-assets-yourname
S3_ACCESS_KEY_ID=your-hetzner-access-key
S3_SECRET_ACCESS_KEY=your-hetzner-secret-key
S3_REGION=fsn1
S3_ENDPOINT=https://fsn1.your-objectstorage.com
S3_FORCE_PATH_STYLE=true
:::note
Replace fsn1 with your chosen location (e.g., nbg1 for Nuremberg). The endpoint URL will match your bucket's location. Ensure the region and endpoint location match.
:::
Install MinIO Server
Option A: Using Docker (Recommended)
# Create a docker-compose.yml file
docker compose up -d minio
Option B: Direct Installation
minio server /data --console-address ":9001"Access MinIO Console
minioadmin / minioadminCreate Access Keys
The MinIO web console in development setups typically only shows bucket management. For access key creation, use the MinIO CLI:
Install MinIO Client (if not already installed):
# macOS
brew install minio/stable/mc
# Linux
curl https://dl.min.io/client/mc/release/linux-amd64/mc \
--create-dirs -o $HOME/minio-binaries/mc
chmod +x $HOME/minio-binaries/mc
export PATH=$PATH:$HOME/minio-binaries/
# Windows
# Download mc.exe from https://dl.min.io/client/mc/release/windows-amd64/mc.exe
Configure and create access keys:
# Set up MinIO client alias (replace with your MinIO server details)
mc alias set local http://localhost:9000 minioadmin minioadmin
# Create a service account (access key pair)
mc admin user svcacct add local minioadmin
# This will output something like:
# Access Key: AKIAIOSFODNN7EXAMPLE
# Secret Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
⚠️ Important: Save both keys immediately as the Secret Key won't be shown again
Create Storage Bucket
vendure-assetsAlternative using CLI:
# Create bucket using MinIO client
mc mb local/vendure-assets
Configure Public Access Policy
For public asset access, set the bucket policy using the MinIO CLI (console UI may not have policy editor):
# Create a policy file for public read access
cat > public-read-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::vendure-assets/*"
}
]
}
EOF
# Apply the policy to the bucket
mc anonymous set download local/vendure-assets
# Or apply the JSON policy directly
mc admin policy create local public-read public-read-policy.json
Alternative simple approach:
# Make bucket publicly readable (simpler method)
mc anonymous set download local/vendure-assets
Environment Variables
# MinIO Configuration
S3_BUCKET=vendure-assets
S3_ACCESS_KEY_ID=minio-access-key
S3_SECRET_ACCESS_KEY=minio-secret-key
S3_REGION=us-east-1
S3_ENDPOINT=http://localhost:9000
S3_FORCE_PATH_STYLE=true
Configure your Vendure application to use S3-compatible asset storage by modifying your vendure-config.ts:
import { VendureConfig } from '@vendure/core';
import { // [!code highlight]
AssetServerPlugin, // [!code highlight]
configureS3AssetStorage // [!code highlight]
} from '@vendure/asset-server-plugin'; // [!code highlight]
import 'dotenv/config';
import path from 'path';
const IS_DEV = process.env.APP_ENV === 'dev';
export const config: VendureConfig = {
// ... other configuration options
plugins: [
AssetServerPlugin.init({ // [!code highlight]
route: 'assets', // [!code highlight]
assetUploadDir: path.join(__dirname, '../static/assets'), // [!code highlight]
assetUrlPrefix: IS_DEV ? undefined : 'https://www.my-shop.com/assets/', // [!code highlight]
// [!code highlight]
// S3-Compatible Storage Configuration // [!code highlight]
// Dynamically switches between local storage and S3 based on environment // [!code highlight]
storageStrategyFactory: process.env.S3_BUCKET // [!code highlight]
? configureS3AssetStorage({ // [!code highlight]
bucket: process.env.S3_BUCKET, // [!code highlight]
credentials: { // [!code highlight]
accessKeyId: process.env.S3_ACCESS_KEY_ID!, // [!code highlight]
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!, // [!code highlight]
}, // [!code highlight]
nativeS3Configuration: { // [!code highlight]
// Platform-specific endpoint configuration // [!code highlight]
endpoint: process.env.S3_ENDPOINT, // [!code highlight]
region: process.env.S3_REGION, // [!code highlight]
forcePathStyle: process.env.S3_FORCE_PATH_STYLE === 'true', // [!code highlight]
signatureVersion: 'v4', // [!code highlight]
}, // [!code highlight]
}) // [!code highlight]
: undefined, // Fallback to local storage when S3 not configured // [!code highlight]
}), // [!code highlight]
// ... other plugins
],
};
:::note
IMPORTANT: The configuration uses a conditional approach - when S3_BUCKET is set, it activates S3 storage; otherwise, it falls back to local file storage. This enables seamless development-to-production transitions.
:::
Create a .env file in your project root with your chosen storage provider configuration:
# Basic Vendure Configuration
APP_ENV=dev
SUPERADMIN_USERNAME=superadmin
SUPERADMIN_PASSWORD=superadmin
COOKIE_SECRET=your-cookie-secret-32-characters-min
# S3-Compatible Storage Configuration
S3_BUCKET=your-bucket-name
S3_ACCESS_KEY_ID=your-access-key-id
S3_SECRET_ACCESS_KEY=your-secret-access-key
S3_REGION=your-region
S3_ENDPOINT=your-endpoint-url
S3_FORCE_PATH_STYLE=true-or-false
:::cli Preconfigured environment examples for each storage provider are available in the s3-file-storage example repository. :::
Verify your S3 storage configuration works correctly:
Start your Vendure server:
npm run dev:server
Access the Dashboard:
Test asset upload:
Verify storage backend:
For production deployments with CDN or custom domains:
AssetServerPlugin.init({
route: 'assets',
assetUrlPrefix: 'https://cdn.yourdomain.com/assets/', // [!code highlight]
storageStrategyFactory: process.env.S3_BUCKET
? configureS3AssetStorage({
// ... S3 configuration
})
: undefined,
});
Use different buckets for different environments:
# Development
S3_BUCKET=vendure-dev-assets
# Staging
S3_BUCKET=vendure-staging-assets
# Production
S3_BUCKET=vendure-prod-assets
Switching between storage providers requires updating only the environment variables:
# From AWS S3 to CloudFlare R2
# Change these variables:
S3_ENDPOINT=https://account-id.r2.cloudflarestorage.com
S3_FORCE_PATH_STYLE=true
# Keep the same bucket name and credentials structure
"Access Denied" Errors:
"Bucket Not Found" Errors:
S3_REGION matches your bucket's regionS3_FORCE_PATH_STYLE=trueAssets Not Loading:
assetUrlPrefix matches your actual domainConnection Timeout Issues:
S3_ENDPOINT URL is correct and accessibleYou now have a robust, platform-agnostic S3-compatible asset storage solution integrated with your Vendure application. This configuration provides:
The unified approach eliminates the need for custom storage plugins while maintaining flexibility across different cloud storage platforms. Your assets will be reliably stored and served regardless of which S3-compatible provider you choose.