Skip to content

DigitalOcean Spaces Integration

Overview

DigitalOcean Spaces is S3-compatible object storage that integrates seamlessly with Django applications using django-storages and boto3.

Django Configuration

Required Packages

Add to requirements.txt:

django-storages[s3]==1.14.2
boto3==1.34.19

Django Settings

# settings.py
import os

# DigitalOcean Spaces Configuration
USE_SPACES = os.getenv('USE_SPACES') == 'TRUE'

if USE_SPACES:
    # AWS S3 settings (DigitalOcean Spaces is S3-compatible)
    AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
    AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
    AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
    AWS_DEFAULT_ACL = 'public-read'
    AWS_S3_ENDPOINT_URL = 'https://nyc3.digitaloceanspaces.com'  # Change region as needed
    AWS_S3_OBJECT_PARAMETERS = {
        'CacheControl': 'max-age=86400',
    }

    # Static files configuration
    AWS_LOCATION = 'static'
    STATIC_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.nyc3.digitaloceanspaces.com/{AWS_LOCATION}/'
    STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

    # Media files configuration
    DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
    MEDIA_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.nyc3.digitaloceanspaces.com/media/'

Django 4.2+ Storage Configuration

For Django 4.2+, use the new STORAGES setting:

STORAGES = {
    "default": {
        "BACKEND": "storages.backends.s3.S3Storage",
        "OPTIONS": {
            "access_key": os.getenv('AWS_ACCESS_KEY_ID'),
            "secret_key": os.getenv('AWS_SECRET_ACCESS_KEY'),
            "bucket_name": os.getenv('AWS_STORAGE_BUCKET_NAME'),
            "endpoint_url": "https://nyc3.digitaloceanspaces.com",
            "default_acl": "public-read",
            "object_parameters": {
                "CacheControl": "max-age=86400",
            },
        },
    },
    "staticfiles": {
        "BACKEND": "storages.backends.s3.S3StaticStorage",
        "OPTIONS": {
            "access_key": os.getenv('AWS_ACCESS_KEY_ID'),
            "secret_key": os.getenv('AWS_SECRET_ACCESS_KEY'),
            "bucket_name": os.getenv('AWS_STORAGE_BUCKET_NAME'),
            "endpoint_url": "https://nyc3.digitaloceanspaces.com",
            "location": "static",
        },
    },
}

Environment Variables

# .env file
USE_SPACES=TRUE
AWS_ACCESS_KEY_ID=your_spaces_access_key
AWS_SECRET_ACCESS_KEY=your_spaces_secret_key
AWS_STORAGE_BUCKET_NAME=your_bucket_name

Boto3 Direct Usage

Basic Setup

import boto3
import os

# Create session
session = boto3.session.Session()
client = session.client(
    's3',
    region_name='nyc3',
    endpoint_url='https://nyc3.digitaloceanspaces.com',
    aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
    aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY')
)

File Operations

# Upload file
with open('local_file.txt', 'rb') as file_contents:
    client.put_object(
        Bucket='your-bucket-name',
        Key='uploads/local_file.txt',
        Body=file_contents,
        ACL='public-read'
    )

# Download file
client.download_file(
    Bucket='your-bucket-name',
    Key='uploads/local_file.txt',
    Filename='downloaded_file.txt'
)

# Generate presigned URL
url = client.generate_presigned_url(
    ClientMethod='get_object',
    Params={'Bucket': 'your-bucket-name', 'Key': 'uploads/local_file.txt'},
    ExpiresIn=3600  # 1 hour
)

DigitalOcean CLI (doctl) Integration

Authentication

# Install doctl
sudo snap install doctl

# Create alias
echo "alias doctl='snap run doctl'" >> ~/.bash_aliases

# Authenticate
doctl auth init --context azmx
# Enter your DigitalOcean API token

# Switch contexts
doctl auth switch --context azmx

Spaces Management

# List spaces
doctl compute spaces list

# Create space
doctl compute spaces create my-new-space --region nyc3

# Upload file
doctl compute spaces cp local_file.txt do:my-space/uploads/

# Download file
doctl compute spaces cp do:my-space/uploads/file.txt local_file.txt

CDN Configuration

For CDN-enabled Spaces

# Note: Use the origin endpoint for boto3, not the CDN endpoint
AWS_S3_ENDPOINT_URL = 'https://nyc3.digitaloceanspaces.com'  # NOT .cdn.digitaloceanspaces.com

# For presigned URLs with CDN
from botocore.client import Config

client = session.client(
    's3',
    region_name='nyc3',
    endpoint_url='https://nyc3.digitaloceanspaces.com',
    aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
    aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),
    config=Config(s3={'addressing_style': 'virtual'})
)

File Upload in Django Views

Example View

from django.shortcuts import render
from django.core.files.storage import default_storage
from django.conf import settings

def upload_file(request):
    if request.method == 'POST' and request.FILES['file']:
        uploaded_file = request.FILES['file']
        file_path = default_storage.save(
            f'uploads/{uploaded_file.name}',
            uploaded_file
        )
        file_url = default_storage.url(file_path)
        return render(request, 'success.html', {'file_url': file_url})

    return render(request, 'upload.html')

Common Issues and Solutions

1. Static Files Not Uploading

Ensure STATIC_ROOT is set even when using Spaces:

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

2. CORS Issues

Configure CORS in DigitalOcean Spaces control panel for web access.

3. ACL Warnings

Django-storages will default to no ACL in version 2.0. Set explicitly:

AWS_DEFAULT_ACL = None  # Use bucket's default ACL
# OR
AWS_DEFAULT_ACL = 'public-read'  # Make files publicly readable

Performance Optimization

Caching Headers

AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',  # 24 hours
    'Expires': 'Thu, 31 Dec 2099 20:00:00 GMT',
}

Compression

# Enable gzip compression for text files
AWS_IS_GZIPPED = True
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}

References