DigitalOcean Spaces
Set up DigitalOcean Spaces with CDN for fast, affordable file uploads
Using DigitalOcean Spaces
Set up DigitalOcean Spaces for fast, affordable file uploads with built-in CDN.
Why Choose DigitalOcean Spaces?
- 💰 Predictable Pricing: Simple pricing with generous free tier
- 🌐 Built-in CDN: Free CDN with all Spaces for global performance
- 🔗 S3 Compatible: Works seamlessly with S3 tools and libraries
- 🚀 Easy Setup: Simple configuration with great developer experience
1. Create a Space
- Go to DigitalOcean Cloud Panel
- Click "Create a Space"
- Choose a datacenter region (closest to your users)
- Enter a unique Space name (e.g.,
my-app-uploads
) - Choose "Restrict File Listing" for security
- Enable "CDN" for global distribution
- Click "Create a Space"
2. Configure CDN (Recommended)
DigitalOcean automatically creates a CDN endpoint:
- Space URL:
https://my-app-uploads.nyc3.digitaloceanspaces.com
- CDN URL:
https://my-app-uploads.nyc3.cdn.digitaloceanspaces.com
Custom Domain (Optional)
Set up your own domain for branded URLs:
- Go to "Settings" in your Space
- Click "Add Custom Domain"
- Enter your domain (e.g.,
uploads.yourdomain.com
) - Add a CNAME record in your DNS:
# DNS Record: uploads.yourdomain.com -> my-app-uploads.nyc3.cdn.digitaloceanspaces.com
3. Generate API Keys
- Go to "API" in your DigitalOcean dashboard
- Click "Spaces access keys"
- Click "Generate New Key"
- Enter a name (e.g., "My App Uploads")
- Save your Access Key and Secret Key
4. Configure CORS
In your Space settings, add this CORS configuration:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
"AllowedOrigins": ["http://localhost:3000", "https://yourdomain.com"],
"ExposeHeaders": ["ETag", "Content-Length"],
"MaxAgeSeconds": 3000
}
]
5. Configure Your App
Add to your .env.local
:
# DigitalOcean Spaces Configuration
AWS_ACCESS_KEY_ID=your_spaces_access_key
AWS_SECRET_ACCESS_KEY=your_spaces_secret_key
AWS_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com
AWS_REGION=nyc3
S3_BUCKET_NAME=your-space-name
# CDN Configuration (recommended)
DIGITALOCEAN_CDN_ENDPOINT=https://your-space-name.nyc3.cdn.digitaloceanspaces.com
# Or your custom domain:
# DIGITALOCEAN_CDN_ENDPOINT=https://uploads.yourdomain.com
6. Update Your Upload Configuration
// lib/upload.ts
import { createUploadConfig } from "pushduck/server";
export const { s3, } = createUploadConfig()
.provider("digitalOceanSpaces",{
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
region: process.env.AWS_REGION!, // e.g., 'nyc3', 'sfo3', 'ams3'
bucket: process.env.S3_BUCKET_NAME!,
// Use CDN for faster file serving
cdnEndpoint: process.env.DIGITALOCEAN_CDN_ENDPOINT,
})
.defaults({
maxFileSize: "50MB", // Spaces supports larger files
acl: "public-read",
})
.build();
7. Test Your Setup
npx @pushduck/cli@latest test --provider digitalocean
This will verify your Spaces connection and upload a test file.
✅ You're Ready!
Your DigitalOcean Space is configured! Benefits:
- Global CDN - Files served from 12+ locations worldwide
- Affordable pricing - $5/month for 250GB + 1TB transfer
- High performance - Built-in CDN acceleration
🌐 CDN Benefits
Automatic Global Distribution
Your files are automatically cached worldwide:
// Files are served from the nearest CDN location
const imageUrl = file.url; // Automatically CDN-accelerated
// CDN locations include:
// - North America: NYC, SF, Toronto
// - Europe: Amsterdam, London, Frankfurt
// - Asia: Singapore, Bangalore
Cache Control
Optimize caching for different file types:
// Configure caching per upload type
const imageUpload = s3
.image()
.max("10MB")
.onUploadComplete(async ({ file, key }) => {
// Set cache headers for images (long cache)
await setObjectMetadata(key, {
"Cache-Control": "public, max-age=31536000", // 1 year
"Content-Type": file.type,
});
});
const documentUpload = s3
.file()
.types(["pdf", "docx"])
.onUploadComplete(async ({ file, key }) => {
// Shorter cache for documents
await setObjectMetadata(key, {
"Cache-Control": "public, max-age=86400", // 1 day
});
});
🔧 Advanced Configuration
Multiple Regions
Deploy across multiple regions for redundancy:
// Primary region (closest to users)
const primarySpaces = createUploadConfig().provider("digitalOceanSpaces",{
region: "nyc3", // New York
bucket: "my-app-uploads-us",
});
// Backup region
const backupSpaces = createUploadConfig().provider("digitalOceanSpaces",{
region: "ams3", // Amsterdam
bucket: "my-app-uploads-eu",
});
Lifecycle Policies
Automatically manage old files:
// Configure automatic cleanup
.hooks({
onUploadComplete: async ({ file, metadata }) => {
// Schedule deletion of temporary files
if (metadata.category === 'temp') {
await scheduleCleanup(file.key, { days: 7 });
}
}
})
💰 Pricing Breakdown
Resource | Included | Overage |
---|---|---|
Storage | 250 GB | $0.02/GB |
Transfer | 1 TB | $0.01/GB |
CDN | Included | Free |
Requests | Unlimited | Free |
Total: $5/month for most small-medium apps
🔒 Security Features
Private Spaces
For sensitive files:
// Configure private access
export const { s3, } = createUploadConfig()
.provider("digitalOceanSpaces",{
// ... config
acl: "private", // Files not publicly accessible
})
.defaults({
generatePresignedUrl: true, // Generate secure URLs
urlExpirationHours: 1, // URLs expire after 1 hour
})
.build();
File Access Control
Control who can access what:
.middleware(async ({ req, file }) => {
const user = await authenticate(req);
// Only allow users to upload to their own folder
const userPrefix = `users/${user.id}/`;
return {
userId: user.id,
keyPrefix: userPrefix,
};
})
📊 Monitoring & Analytics
Usage Monitoring
Track your Space usage:
// Monitor uploads in real-time
.hooks({
onUploadStart: async ({ file }) => {
await analytics.track("upload_started", {
provider: "digitalocean-spaces",
fileSize: file.size,
fileType: file.type,
});
},
onUploadComplete: async ({ file, metadata }) => {
await analytics.track("upload_completed", {
provider: "digitalocean-spaces",
duration: metadata.uploadTime,
cdnEnabled: true,
});
}
})
🆘 Common Issues
CORS errors? → Verify your domain is in AllowedOrigins and CORS is enabled
Slow uploads? → Check you're using the correct regional endpoint
CDN not working? → Verify CDN is enabled and using the cdn.digitaloceanspaces.com endpoint
Access denied? → Check your API keys have Spaces read/write permissions
File not found? → Ensure you're using the CDN endpoint for file access
🚀 Performance Tips
- Use CDN endpoints for all file access (not direct Space URLs)
- Choose closest region to your primary user base
- Enable gzip compression for text files
- Set proper cache headers for different file types
- Use progressive image formats (WebP, AVIF) when possible
Next: Upload Your First Image or try MinIO Setup