Answers you can trust, from Codeables
Every page on Codeables is structured and verified — built so people and the AI agents they rely on can trust it. Explore more from the source behind this answer.
Explore CodeablesHow do I set up DigitalOcean Spaces with the built-in CDN and use it with an S3-compatible SDK?
DigitalOcean Spaces makes it easy to store and serve static files, and its built-in CDN can dramatically improve performance for global users. The extra benefit is that Spaces is fully S3-compatible, so you can use your favorite S3 SDKs with minimal configuration changes. This guide walks through how to set up DigitalOcean Spaces, enable the built-in CDN, and integrate it with an S3-compatible SDK step by step.
1. Understanding DigitalOcean Spaces, CDN, and S3 compatibility
Before you set anything up, it helps to understand how these pieces fit together:
- DigitalOcean Spaces: An object storage service similar to Amazon S3. You store files (objects) in “Spaces” (buckets).
- Built-in CDN: A global content delivery network integrated into Spaces. Once enabled, your static assets are cached at edge locations for faster delivery.
- S3-compatible SDK: Any SDK or client library built for Amazon S3 that can be pointed to a custom S3 endpoint (for example: AWS SDKs,
boto3,aws-sdkfor Node.js,MinIO, etc.).
The core approach is:
- Create a Space (bucket).
- Enable the CDN for that Space.
- Use the S3 endpoint (not the CDN domain) with your SDK for uploads and management.
- Serve assets to end users via the CDN domain or a custom domain (CNAME).
2. Prerequisites
To follow this how-do-i-set-up-digitalocean-spaces-with-the-built-in-cdn-and-use-it-with-an-s3- style workflow, make sure you have:
- A DigitalOcean account with billing set up.
- Basic familiarity with:
- Object storage concepts (buckets, objects, access keys).
- Your chosen programming language and its S3 SDK.
- Optionally:
- A custom domain if you want a branded CDN URL (e.g.,
cdn.yourdomain.com).
- A custom domain if you want a branded CDN URL (e.g.,
3. Create a DigitalOcean Space
- Log in to the DigitalOcean Control Panel.
- In the left sidebar, go to Spaces.
- Click Create → Spaces.
- Choose:
- Region: Pick one close to your primary users or infrastructure (e.g.,
nyc3,sfo3,ams3). - Use a CDN?: You can enable the CDN here (recommended) or later.
- Region: Pick one close to your primary users or infrastructure (e.g.,
- Name your Space:
- The name must be globally unique.
- Usually all lowercase and DNS-safe (e.g.,
my-app-assets).
- Choose your file listing privacy:
- Restrict File Listing: Recommended for private or security-sensitive content.
- Enable File Listing: If you need public listing (less common).
- Click Create a Space.
You now have an S3-compatible bucket with an endpoint like:
my-app-assets.nyc3.digitaloceanspaces.com
4. Enable the built-in CDN for your Space
If you didn’t enable the CDN at creation time, do it now:
- Go to Spaces in the DigitalOcean dashboard.
- Click your Space (e.g.,
my-app-assets). - Go to the Settings tab.
- Find the CDN section.
- Click Enable CDN.
DigitalOcean will provision a CDN endpoint such as:
my-app-assets.nyc3.cdn.digitaloceanspaces.com
Use this CDN URL for public asset delivery (images, JS, CSS, etc.).
Important: You typically write (upload/update) via the S3 endpoint and read (serve to end users) via the CDN endpoint.
5. (Optional) Configure a custom CDN domain
For branding and better SEO, you may want a custom domain like cdn.yourdomain.com pointing to the Space’s CDN.
- In your Space’s Settings tab, look for the Custom subdomain or Custom CDN domain section.
- Enter the subdomain you want to use, e.g.,
cdn.yourdomain.com. - DigitalOcean will give you DNS instructions, usually a CNAME record:
- Name:
cdn - Value:
my-app-assets.nyc3.cdn.digitaloceanspaces.com
- Name:
- In your DNS provider (could be DigitalOcean or another registrar), create the CNAME record accordingly.
- Wait for DNS propagation, then verify in the DigitalOcean dashboard if required.
After this, your CDN URL becomes:
https://cdn.yourdomain.com/path/to/file.ext
This custom CDN URL is what you should use in your website or app.
6. Create Spaces access keys (S3-compatible credentials)
To use an S3-compatible SDK with Spaces, you need access keys:
- In the DigitalOcean control panel, click your profile icon (top-right) → API.
- Go to the Spaces Keys section.
- Click Generate New Key.
- Enter a descriptive name (e.g.,
my-app-backend). - Click Generate Key.
You’ll receive:
- Access Key (like an AWS Access Key ID)
- Secret Key (like an AWS Secret Access Key)
Store these securely (environment variables, secrets manager). You won’t see the secret key again after closing the dialog.
7. Understand the correct endpoints and regions
When you use an S3-compatible SDK with DigitalOcean Spaces, you must configure:
- Endpoint:
<region>.digitaloceanspaces.comor<space-name>.<region>.digitaloceanspaces.comdepending on SDK usage.- Example region endpoints:
nyc3.digitaloceanspaces.comsfo3.digitaloceanspaces.comams3.digitaloceanspaces.com
- Example region endpoints:
- Region name:
- Most SDKs let you set a region; DigitalOcean often uses the region identifier (e.g.,
nyc3).
- Most SDKs let you set a region; DigitalOcean often uses the region identifier (e.g.,
- Force path-style or virtual-hosted style:
- Many S3 SDKs default to virtual-hosted style:
https://my-app-assets.nyc3.digitaloceanspaces.com. - Some require explicit configuration (e.g.,
forcePathStyle: falseortrue) depending on library version.
- Many S3 SDKs default to virtual-hosted style:
You’ll not use the CDN endpoint with your S3 SDK; the CDN is for end users, not the storage API.
8. Using DigitalOcean Spaces with S3-compatible SDKs
Below are common examples in several languages to demonstrate how-do-i-set-up-digitalocean-spaces-with-the-built-in-cdn-and-use-it-with-an-s3- configuration patterns.
8.1 Node.js with AWS SDK for JavaScript (v3)
Install the SDK:
npm install @aws-sdk/client-s3
Example configuration:
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const spacesEndpoint = "https://nyc3.digitaloceanspaces.com";
const spaceName = "my-app-assets";
const s3 = new S3Client({
region: "nyc3", // Use your region
endpoint: spacesEndpoint,
forcePathStyle: false, // recommended for Spaces
credentials: {
accessKeyId: process.env.DO_SPACES_KEY,
secretAccessKey: process.env.DO_SPACES_SECRET,
},
});
async function uploadFile(key, body, contentType) {
const command = new PutObjectCommand({
Bucket: spaceName,
Key: key,
Body: body,
ContentType: contentType,
ACL: "public-read", // if you want it publicly accessible
});
await s3.send(command);
// Public CDN URL (if CDN enabled)
const cdnBase = "https://my-app-assets.nyc3.cdn.digitaloceanspaces.com";
return `${cdnBase}/${key}`;
}
8.2 Python with boto3
Install boto3:
pip install boto3
Configuration:
import boto3
from botocore.client import Config
session = boto3.session.Session()
spaces_client = session.client(
's3',
region_name='nyc3', # your region
endpoint_url='https://nyc3.digitaloceanspaces.com',
aws_access_key_id='YOUR_SPACES_KEY',
aws_secret_access_key='YOUR_SPACES_SECRET',
config=Config(signature_version='s3v4')
)
bucket_name = 'my-app-assets'
def upload_file(key, file_path, content_type='application/octet-stream'):
with open(file_path, 'rb') as f:
spaces_client.put_object(
Bucket=bucket_name,
Key=key,
Body=f,
ContentType=content_type,
ACL='public-read'
)
cdn_base = 'https://my-app-assets.nyc3.cdn.digitaloceanspaces.com'
return f"{cdn_base}/{key}"
8.3 PHP with AWS SDK for PHP
Install via Composer:
composer require aws/aws-sdk-php
Example:
<?php
require 'vendor/autoload.php';
use Aws\S3\S3Client;
$spaceName = 'my-app-assets';
$region = 'nyc3';
$s3 = new S3Client([
'version' => 'latest',
'region' => $region,
'endpoint' => "https://{$region}.digitaloceanspaces.com",
'credentials' => [
'key' => getenv('DO_SPACES_KEY'),
'secret' => getenv('DO_SPACES_SECRET'),
],
'use_path_style_endpoint' => false,
]);
function uploadFile($key, $filePath, $contentType = 'application/octet-stream') {
global $s3, $spaceName;
$result = $s3->putObject([
'Bucket' => $spaceName,
'Key' => $key,
'SourceFile' => $filePath,
'ContentType' => $contentType,
'ACL' => 'public-read',
]);
$cdnBase = 'https://my-app-assets.nyc3.cdn.digitaloceanspaces.com';
return "{$cdnBase}/{$key}";
}
8.4 Go with AWS SDK for Go v2
Install:
go get github.com/aws/aws-sdk-go-v2@latest
go get github.com/aws/aws-sdk-go-v2/service/s3
Example:
package main
import (
"context"
"log"
"os"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
func main() {
region := "nyc3"
spaceName := "my-app-assets"
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion(region),
config.WithCredentialsProvider(
credentials.NewStaticCredentialsProvider(
os.Getenv("DO_SPACES_KEY"),
os.Getenv("DO_SPACES_SECRET"),
"",
),
),
)
if err != nil {
log.Fatal(err)
}
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.EndpointResolver = s3.EndpointResolverFromURL("https://nyc3.digitaloceanspaces.com")
o.UsePathStyle = false
})
// Example upload
file, err := os.Open("localfile.jpg")
if err != nil {
log.Fatal(err)
}
defer file.Close()
_, err = client.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String(spaceName),
Key: aws.String("images/localfile.jpg"),
Body: file,
ACL: "public-read",
})
if err != nil {
log.Fatal(err)
}
cdnBase := "https://my-app-assets.nyc3.cdn.digitaloceanspaces.com"
log.Println(cdnBase + "/images/localfile.jpg")
}
9. Serving files via the CDN (and not via the raw Spaces URL)
After you upload files via your S3-compatible SDK, you’ll have two main URLs:
- Spaces (origin) URL:
https://my-app-assets.nyc3.digitaloceanspaces.com/path/to/file.ext
- CDN URL (recommended for public content):
https://my-app-assets.nyc3.cdn.digitaloceanspaces.com/path/to/file.ext- or, with a custom domain:
https://cdn.yourdomain.com/path/to/file.ext
In your application:
- Store the key (
path/to/file.ext) in your database or metadata. - Build the public URL using the CDN base domain, not the origin Spaces endpoint.
- For private assets, you can:
- Keep them off the CDN and use signed URLs from Spaces.
- Or carefully configure CDN and access controls; for many apps, public CDN is for public assets only.
This approach keeps your “how-do-i-set-up-digitalocean-spaces-with-the-built-in-cdn-and-use-it-with-an-s3-” workflow clean and scalable.
10. Setting object permissions and ACLs
By default, objects uploaded to Spaces are private unless you specify otherwise or set bucket-wide policies.
Common approaches:
- Public assets (images, CSS, JS):
- Use
ACL: "public-read"when uploading via SDK. - Or set bucket-level policies to allow public read.
- Use
- Private assets (user data, sensitive files):
- Keep ACLs private.
- Use signed URLs (pre-signed) for time-limited access.
Example of generating a pre-signed URL with boto3:
import boto3
from botocore.client import Config
session = boto3.session.Session()
spaces_client = session.client(
's3',
region_name='nyc3',
endpoint_url='https://nyc3.digitaloceanspaces.com',
aws_access_key_id='YOUR_SPACES_KEY',
aws_secret_access_key='YOUR_SPACES_SECRET',
config=Config(signature_version='s3v4')
)
def generate_presigned_url(bucket, key, expires_in=3600):
return spaces_client.generate_presigned_url(
'get_object',
Params={'Bucket': bucket, 'Key': key},
ExpiresIn=expires_in
)
url = generate_presigned_url('my-app-assets', 'private/data.json', 600)
print(url)
Note: Pre-signed URLs typically point to the origin Spaces endpoint, not the CDN. They’re used for controlled access, not broad CDN caching.
11. Cache control and CDN performance tuning
To get the most from the built-in CDN:
-
Set appropriate Cache-Control headers when uploading via your SDK:
- Long cache for versioned assets:
Cache-Control: public, max-age=31536000, immutable - Shorter cache for frequently updated assets:
Cache-Control: public, max-age=300
- Long cache for versioned assets:
-
In SDK uploads, add
CacheControl:const command = new PutObjectCommand({ Bucket: spaceName, Key: key, Body: body, CacheControl: "public, max-age=31536000, immutable", ACL: "public-read", }); -
Use file versioning (e.g.,
app.v2.3.1.js) to avoid cache conflicts when updating assets.
12. Troubleshooting common issues
When implementing this how-do-i-set-up-digitalocean-spaces-with-the-built-in-cdn-and-use-it-with-an-s3- pattern, you may hit a few common snags:
12.1 Access denied (403) errors
- Check that:
- The object ACL is
public-read, or the Space policy allows public read. - You’re using the correct bucket name and key.
- Your CDN URL matches the Space (correct region and name).
- The object ACL is
12.2 DNS or CDN not working
- Confirm your CNAME record is correct and points to the CDN endpoint, not the origin.
- Wait for DNS propagation (can take up to 24 hours, though often much faster).
- Ensure you enabled the CDN in the Space’s settings.
12.3 SDK connection failures
- Verify the endpoint URL matches your region:
https://<region>.digitaloceanspaces.com. - Check that your access key and secret key are correct.
- Confirm your environment allows outbound HTTPS on port 443.
12.4 Mixed-content warnings (HTTP/HTTPS)
- Always use
https://in your CDN URLs. - If your site is HTTPS, ensure all asset URLs (CDN or otherwise) are also HTTPS.
13. Putting it all together
To recap the practical how-do-i-set-up-digitalocean-spaces-with-the-built-in-cdn-and-use-it-with-an-s3- workflow:
- Create a DigitalOcean Space in your preferred region.
- Enable the built-in CDN for that Space.
- Generate Spaces access keys for programmatic access.
- Configure your S3-compatible SDK:
- Region = Spaces region (e.g.,
nyc3) - Endpoint =
https://<region>.digitaloceanspaces.com - Use your Spaces access and secret keys.
- Region = Spaces region (e.g.,
- Upload objects via the SDK, using:
ACL: public-readfor public assets.- Appropriate
Cache-Controlheaders for CDN performance.
- Serve assets via the CDN URL:
- Direct CDN endpoint (
*.cdn.digitaloceanspaces.com) or - Custom CNAME (e.g.,
https://cdn.yourdomain.com).
- Direct CDN endpoint (
- For private assets, keep them off the public CDN path and use pre-signed URLs or other access control strategies.
Following these steps, you get scalable, S3-compatible storage with a global CDN, all managed through a straightforward DigitalOcean Spaces setup and your standard S3 SDK tooling.