DigitalOcean Spaces Object Storage Guide
Guide to Spaces S3-compatible object storage covering creation, access control, CDN, CORS, lifecycle policies, and application integration with code examples.
Prerequisites
- DigitalOcean account with Spaces access keys generated
- Basic understanding of HTTP and REST APIs
DigitalOcean Spaces Object Storage
Spaces is DigitalOcean's S3-compatible object storage service designed for storing and serving large amounts of unstructured data such as images, videos, backups, logs, and static website assets. Spaces provides a simple, predictable pricing model: $5/month includes 250 GB of storage and 1 TB of outbound bandwidth, with additional storage at $0.02/GB and bandwidth at $0.01/GB. This straightforward pricing stands in contrast to S3's complex tiering and request-based pricing.
Because Spaces is S3-compatible, you can use any S3-compatible tool, library, or SDK to interact with it. This includes the AWS CLI, s3cmd, Cyberduck, boto3, aws-sdk, and many backup tools that support S3 backends. This guide covers Space creation, access management, CDN configuration, lifecycle policies, CORS setup, and integration patterns.
Creating and Configuring Spaces
Using doctl
# Note: doctl does not yet support Spaces creation directly.
# Use the API or control panel for Space creation.
# Create a Space via the API
curl -X POST "https://api.digitalocean.com/v2/spaces" \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "my-assets", "region": "nyc3"}'Using s3cmd
The s3cmd tool provides a command-line interface for S3-compatible storage. Configure it with your Spaces access keys (generated in the control panel under API > Spaces Keys):
# Configure s3cmd for Spaces
cat > ~/.s3cfg << 'ENDCFG'
[default]
access_key = YOUR_SPACES_ACCESS_KEY
secret_key = YOUR_SPACES_SECRET_KEY
host_base = nyc3.digitaloceanspaces.com
host_bucket = %(bucket)s.nyc3.digitaloceanspaces.com
ENDCFG
# Create a bucket (Space)
s3cmd mb s3://my-assets
# Upload a file
s3cmd put logo.png s3://my-assets/images/logo.png
# Upload a directory
s3cmd sync ./static/ s3://my-assets/static/ --acl-public
# List objects
s3cmd ls s3://my-assets/
# Download a file
s3cmd get s3://my-assets/images/logo.png ./downloaded-logo.pngAccess Control
Spaces Access Keys
Spaces uses API keys separate from your main DigitalOcean API token. Generate Spaces access keys from API > Spaces Keys in the control panel. Each key pair consists of an Access Key and a Secret Key, similar to AWS IAM access keys. You can create multiple key pairs for different applications or environments.
Bucket Policies and ACLs
Spaces supports both bucket-level ACLs and object-level ACLs. By default, Spaces are private. You can make an entire Space public (all objects readable without authentication) or set individual objects as public while keeping the Space private. For fine-grained access control, use pre-signed URLs that grant temporary access to specific objects.
# Make a Space public
s3cmd setacl --acl-public s3://my-assets
# Make a specific object public
s3cmd setacl --acl-public s3://my-assets/images/logo.png
# Generate a pre-signed URL (expires in 1 hour)
s3cmd signurl s3://my-assets/private/report.pdf 3600CDN Integration
Spaces includes a built-in CDN (Content Delivery Network) that caches your content at edge locations worldwide. Enabling CDN on a Space creates a CDN endpoint URL that serves content with low latency from the closest edge location. CDN bandwidth is included in your Spaces bandwidth allowance at no additional cost.
# Enable CDN on a Space (via API)
curl -X POST "https://api.digitalocean.com/v2/cdn/endpoints" \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"origin": "my-assets.nyc3.digitaloceanspaces.com",
"ttl": 3600,
"custom_domain": "cdn.example.com",
"certificate_id": "<cert-id>"
}'CDN Cache Invalidation
When you update objects in your Space, the CDN serves the cached version until the TTL expires. To serve updated content immediately, purge the CDN cache from the control panel or API. For frequently updated content, use versioned filenames (e.g., style.abc123.css) instead of purging, which is more efficient and reliable. The default TTL is 3600 seconds (1 hour) and can be configured from 60 seconds to 2,592,000 seconds (30 days).
CORS Configuration
If your web application loads assets directly from Spaces (fonts, images, API responses), you need to configure Cross-Origin Resource Sharing (CORS) rules. CORS controls which domains can access resources in your Space from a web browser.
<!-- Example CORS configuration -->
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>https://example.com</AllowedOrigin>
<AllowedOrigin>https://www.example.com</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
<MaxAgeSeconds>3600</MaxAgeSeconds>
</CORSRule>
</CORSConfiguration># Apply CORS configuration
s3cmd setcors cors.xml s3://my-assetsLifecycle Policies
Lifecycle policies automate object management by defining rules for when objects should be deleted or transitioned. This is useful for log rotation, temporary file cleanup, and compliance-driven data retention. DigitalOcean Spaces supports expiration rules that automatically delete objects after a specified number of days.
<!-- Lifecycle policy: delete logs after 90 days -->
<LifecycleConfiguration>
<Rule>
<ID>DeleteOldLogs</ID>
<Prefix>logs/</Prefix>
<Status>Enabled</Status>
<Expiration>
<Days>90</Days>
</Expiration>
</Rule>
<Rule>
<ID>CleanupIncompleteUploads</ID>
<Prefix></Prefix>
<Status>Enabled</Status>
<AbortIncompleteMultipartUpload>
<DaysAfterInitiation>7</DaysAfterInitiation>
</AbortIncompleteMultipartUpload>
</Rule>
</LifecycleConfiguration># Apply lifecycle policy
s3cmd setlifecycle lifecycle.xml s3://my-assetsUsing Spaces with Application Code
Node.js (AWS SDK v3)
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "https://nyc3.digitaloceanspaces.com",
region: "us-east-1", // Required by SDK but not used by Spaces
credentials: {
accessKeyId: process.env.SPACES_KEY,
secretAccessKey: process.env.SPACES_SECRET,
},
});
// Upload a file
await s3.send(new PutObjectCommand({
Bucket: "my-assets",
Key: "uploads/photo.jpg",
Body: fileBuffer,
ContentType: "image/jpeg",
ACL: "public-read",
}));
// Generate a CDN URL
const cdnUrl = "https://my-assets.nyc3.cdn.digitaloceanspaces.com/uploads/photo.jpg";Python (boto3)
import boto3
session = boto3.session.Session()
client = session.client(
"s3",
endpoint_url="https://nyc3.digitaloceanspaces.com",
region_name="us-east-1",
aws_access_key_id=os.environ["SPACES_KEY"],
aws_secret_access_key=os.environ["SPACES_SECRET"],
)
# Upload a file
client.upload_file("local-file.pdf", "my-assets", "documents/file.pdf")
# Generate a pre-signed URL
url = client.generate_presigned_url(
"get_object",
Params={"Bucket": "my-assets", "Key": "documents/file.pdf"},
ExpiresIn=3600,
)Common Use Cases
- Static asset hosting: Serve images, CSS, JavaScript, and fonts from Spaces CDN for fast global delivery.
- User uploads: Store user-generated content (profile photos, documents) with pre-signed URLs for secure access.
- Application backups: Store database dumps, configuration backups, and disaster recovery archives.
- Log archival: Ship application and server logs to Spaces with lifecycle policies for automatic expiration.
- Media storage: Store video, audio, and image files with CDN delivery for streaming applications.
- Terraform state: Use Spaces as a remote backend for Terraform state files with locking support.
Spaces Limitations
Spaces has a maximum object size of 5 TB (via multipart upload) and supports up to 10,000 objects per listing request. There is no storage class tiering (unlike S3's Standard, IA, Glacier). All objects are stored on SSD at the same $0.02/GB rate. If you need archival storage at lower cost, consider using S3-compatible lifecycle rules to delete old data rather than tiering it. Spaces does not support S3 Object Lock, server-side encryption with custom keys, or S3 Select.
Cost Optimization
- Use lifecycle policies to automatically delete old logs, temporary files, and outdated backups.
- Enable CDN for frequently accessed public content to reduce origin bandwidth usage.
- Clean up incomplete multipart uploads with lifecycle rules to avoid phantom storage charges.
- Use versioned filenames for static assets instead of CDN purging for cache invalidation.
- Monitor storage growth through the control panel to identify unexpectedly large Spaces.
- Compress before uploading where possible (gzip for text, WebP for images) to reduce storage and bandwidth costs.
Key Takeaways
- 1Spaces is S3-compatible and works with any S3 SDK, CLI, or tool.
- 2$5/month includes 250 GB storage and 1 TB bandwidth with simple overage pricing.
- 3Built-in CDN serves content from global edge locations at no extra cost.
- 4CORS configuration is required for browser-based access from web applications.
- 5Lifecycle policies automate object expiration and multipart upload cleanup.
- 6Pre-signed URLs provide temporary, secure access to private objects.
Frequently Asked Questions
Is Spaces cheaper than S3?
Can I use the AWS CLI with Spaces?
Does Spaces support versioning?
Written by CloudToolStack Team
Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.
Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.