How to use DigitalOcean Spaces with the AWS S3 SDKs?

November 10, 2017 28.5k views
Node.js PHP Python Development Go

DigitalOcean Spaces was designed to be inter-operable with the AWS S3 API in order allow users to continue using the tools they are already working with. In most cases, using Spaces with an existing S3 library requires configuring the endpoint value to be ${REGION} Though often how to change that setting is not well documented as examples tend to use the default AWS values. Third-party libraries tend to be better with this as they will support alternative, self-hosted object storage implementations like Mino or Ceph.

In the answers, let’s share some basic examples of working with Spaces using the AWS SDKs in various languages.

14 Answers

PHP - (AWS docs)


// Included aws/aws-sdk-php via Composer's autoloader
// Installed with: composer.phar require aws/aws-sdk-php
require 'vendor/autoload.php';
use Aws\S3\S3Client;

// Configure a client using Spaces
$client = new Aws\S3\S3Client([
        'version' => 'latest',
        'region'  => 'nyc3',
        'endpoint' => '',
        'credentials' => [
                'key'    => 'ACCESS_KEY',
                'secret' => 'SECRET_KEY',

// Create a new Space
    'Bucket' => 'my-new-space-with-a-unique-name',

// Listing all Spaces in the region
$spaces = $client->listBuckets();
foreach ($spaces['Buckets'] as $space){
    echo $space['Name']."\n";

// Upload a file to the Space
$insert = $client->putObject([
     'Bucket' => 'my-new-space-with-a-unique-name',
     'Key'    => 'file.ext',
     'Body'   => 'The contents of the file'

JavaScript - (AWS docs)

const AWS = require('aws-sdk')

// Configure client for use with Spaces
const spacesEndpoint = new AWS.Endpoint('');
const s3 = new AWS.S3({
    endpoint: spacesEndpoint,
    accessKeyId: 'ACCESS_KEY',
    secretAccessKey: 'SECRET_KEY'

// Create a new Space
var params = {
    Bucket: "my-new-space-with-a-unique-name"

s3.createBucket(params, function(err, data) {
    if (err) console.log(err, err.stack);
    else     console.log(data);

// List all Spaces in the region
s3.listBuckets({}, function(err, data) {
    if (err) console.log(err, err.stack);
        else {
            data['Buckets'].forEach(function(space) {

// Add a file to a Space
var params = {
    Body: "The contents of the file",
    Bucket: "my-new-space-with-a-unique-name",
    Key: "file.ext",

s3.putObject(params, function(err, data) {
    if (err) console.log(err, err.stack);
    else     console.log(data);
  • Also, setting custom headers since some of them are required:

          await S3.putObject(params)
            .on('build', request => {
              request.httpRequest.headers.Host = `https://${BUCKET}.${REGION}`;
              request.httpRequest.headers['Content-Length'] = file.size;
              request.httpRequest.headers['Content-Type'] = file.mimetype;
              request.httpRequest.headers['x-amz-acl'] = 'public-read';
            .send((err, data) => {
              if (err) console.log(err, err.stack);
              else console.log(data);
  • I don’t know if it was just my specific setup, but this did not work for me. I got a series of cryptic messages, which eventually boiled down to the keys not being passed in correctly and aws-sdk looking for ~/.aws/credentials

    This worked for me:

      const s3 = new AWS.S3({
        endpoint: spacesEndpoint,
        credentials: new AWS.Credentials({
          accessKeyId: 'ACCESS_KEY',
          secretAccessKey: 'SECRET_KEY'
    • Okay, looks like both methods actually work. However, if you pass the key values directly to the constructor and they are invalid you will get a horrible error message that tries it’s hardest to look like some kind of network problem. If you pass a Credentials object to the S3 constructor and they are invalid keys then it will give you a much more sensible “Missing credentials” message.

  • I’m trying to create bucket and I’m receiving a not explanatory error response (InvalidRequest: Malformed request), any ideas?

    { InvalidRequest: Malformed request
        at Request.extractError (/var/nodejs/storage/node_modules/aws-sdk/lib/services/s3.js:577:35)
        at Request.callListeners (/var/nodejs/storage/node_modules/aws-sdk/lib/sequential_executor.js:105:20)
        at Request.emit (/var/nodejs/storage/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
        at Request.emit (/var/nodejs/storage/node_modules/aws-sdk/lib/request.js:683:14)
        at Request.transition (/var/nodejs/storage/node_modules/aws-sdk/lib/request.js:22:10)
        at AcceptorStateMachine.runTo (/var/nodejs/storage/node_modules/aws-sdk/lib/state_machine.js:14:12)
        at /var/nodejs/storage/node_modules/aws-sdk/lib/state_machine.js:26:10
        at Request.<anonymous> (/var/nodejs/storage/node_modules/aws-sdk/lib/request.js:38:9)
        at Request.<anonymous> (/var/nodejs/storage/node_modules/aws-sdk/lib/request.js:685:12)
        at Request.callListeners (/var/nodejs/storage/node_modules/aws-sdk/lib/sequential_executor.js:115:18)
      message: 'Malformed request',
      code: 'InvalidRequest',
      region: null,
      time: 2018-05-03T22:16:36.740Z,
      requestId: null,
      extendedRequestId: undefined,
      cfId: undefined,
      statusCode: 400,
      retryable: false,
      retryDelay: 52.89127599113917 }

Working C# example using Amazon AWSSDK (V2.x).

IAmazonS3 amazonS3Client = 
    AWSClientFactory.CreateAmazonS3Client("your-spaces-key", "your-spaces-key-secrete",
    new AmazonS3Config
       ServiceURL = ""
var myBuckets = amazonS3Client.ListBuckets();

c# core 2.1 - list objects

(I use this to verify my automated backup to spaces from an internal company core web application)

Add Nuget package:

dotnet add package AWSSDK.S3 

Add references:

using System;
using System.Collections.Generic;
using System.Linq;
using Amazon.S3;

Declare constants for authentication:

private const string S3_SECRET_KEY = "your-secret-key-value";
private const string S3_ACCESS_KEY = "your-access-key-value";
private const string S3_HOST_ENDPOINT = "";
private const string S3_BUCKET_NAME = "your-bucket-name-here";

Sample method to fetch all filenames stored in Spaces bucket:

 public static List<string> GetFileListFromSpacesBackupStorage()
            AmazonS3Config ClientConfig = new AmazonS3Config();
            ClientConfig.ServiceURL = S3_HOST_ENDPOINT;
            IAmazonS3 s3Client = new AmazonS3Client(S3_ACCESS_KEY, S3_SECRET_KEY, ClientConfig);
            var ObjectList = s3Client.ListObjectsAsync(S3_BUCKET_NAME).Result;
            var FileList = ObjectList.S3Objects.Select(c => c.Key).ToList();
            return FileList;
  • Hi, thanks for the solution. That eased the process up significantly.

    I am, however, experiencing problems when trying to access private files specifically with a whitespace in the filename. They have been successfully uploaded using Minio.

    Has anyone experienced this and maybe even found a solution to this?

Ruby - (AWS docs)

require 'aws-sdk-s3'

# Configure client for use with Spaces
client =
  access_key_id: 'ACCESS_KEY',
  secret_access_key: 'SECRET_KEY',
  endpoint: '',
  region: 'nyc3'

# Create a new Space
  bucket: "my-new-space-with-a-unique-name",
  acl: "private"

# List all Spaces
spaces =  client.list_buckets()
spaces.buckets.each do |space|
  puts "#{}"

# Add a file to a Space
  body: "The contents of the file",
  bucket: "my-new-space-with-a-unique-name",
  key: "file-name.txt"

Go - (AWS Docs)

package main

import (


func main() {
    // Initialize a client using Spaces
    s3Config := &aws.Config{
        Credentials: credentials.NewStaticCredentials("ACCESS_KEY", "SECRET_KEY", ""),
        Endpoint:    aws.String(""),
        Region:      aws.String("us-east-1"), // This is counter intuitive, but it will fail with a non-AWS region name.

    newSession := session.New(s3Config)
    s3Client := s3.New(newSession)

    // Create a new Space
    params := &s3.CreateBucketInput{
        Bucket: aws.String("my-new-space-with-a-unique-name"),

    _, err := s3Client.CreateBucket(params)
    if err != nil {

    // List all Spaces in the region
    spaces, err := s3Client.ListBuckets(nil)
    if err != nil {

    for _, b := range spaces.Buckets {
        fmt.Printf("%s\n", aws.StringValue(b.Name))

    // Upload a file to the Space
    object := s3.PutObjectInput{
        Body:   strings.NewReader("The contents of the file"),
        Bucket: aws.String("my-new-space-with-a-unique-name"),
        Key:    aws.String("file.ext"),
    _, err = s3Client.PutObject(&object)
    if err != nil {

Kotlin (& Java) - [SDK]

You only need to setup “withEndpointConfiguration” correctly and then you can use others API normally, example here or in Amazon official document

var s3Client = AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration("", "sgp1"))
.withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials("key", "secret")))


Python 3 - (Boto Docs)

import boto3

# Initialize a session using Spaces
session = boto3.session.Session()
client = session.client('s3',

# Create a new Space

# List all Spaces in the region
response = client.list_buckets()
for s in [space['Name'] for space in response['Buckets']]:

# Add a file to a Space

We are trying to access the Private files from Spaces using Elfinder File Manager. But not able to access when it is having Private Access. So we are not able to access unless we give them Public Access which is may not good for the application. If we give Public access people can access using URL through any browser without any permissions.

We are able to upload files to the Spaces also, but not able to access same file that we just uploaded. So we would like to upload/access the files with Private access through Elfinder File Manager.

Currently we are using S3 Adapter. Appreciate if any suggestions.

Thank you very much
Venu Kommu

with swift ? also should i need to INFO.list update if yes do have one EXAMPLE, please share?

swift, iOS

var transferUtility: AWSS3TransferUtility!
override func viewDidLoad() {
        // Do any additional setup after loading the view, typically from a nib.
        //Setup credentials
        let credentialsProvider = AWSStaticCredentialsProvider(accessKey:"ACCESS_KEY", secretKey: "SECRET_KEY")

        //Setup region
        let endpoint = AWSEndpoint(urlString: "")

        //Setup the service configuration
        let configuration = AWSServiceConfiguration(region: .USEast1, endpoint: endpoint, credentialsProvider: credentialsProvider)

       //Setup the transfer utility configuration
        let tuConf = AWSS3TransferUtilityConfiguration()
        tuConf.isAccelerateModeEnabled = false
        tuConf.bucket = S3BucketName

        //Register a transfer utility object
            with: configuration!,
            transferUtilityConfiguration: tuConf,
            forKey: "transfer-utility-with-advanced-options"

        //Look up the transfer utility object from the registry to use for your transfers.
        transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "transfer-utility-with-advanced-options")

‘VB.NET Upload files to Digitalocean Spaces with S3 TransferUtility


s3Client = New AmazonS3Client(AWS_ACCESS_KEY, AWS_SECRET_KEY, New AmazonS3Config With {
                        .SignatureVersion = 2,
                        .ServiceURL = ""})

Dim directoryTransferUtility = New TransferUtility(s3Client)
                Dim uploadRequest = New TransferUtilityUploadDirectoryRequest With {
                                .BucketName = spaceName,
                                .KeyPrefix = "folder/",
                                .Directory = "Your Local Directory"

AddHandler uploadRequest.UploadDirectoryProgressEvent, New EventHandler(Of UploadDirectoryProgressArgs)(AddressOf UploadFile_ProgressBar)
Await directoryTransferUtility.UploadDirectoryAsync(uploadRequest)

Catch ex As AmazonS3Exception
MsgBox("Error encountered on server. Message:'{0}' when writing an object", ex.Message.ToString)
Catch ex As Exception
MsgBox("Unknown encountered on server. Message:'{0}' when writing an object", ex.Message.ToString)

End Try

edited by asb

How do you do this in Swift the above solution works but how do you upload?

Have another answer? Share your knowledge.