Have you ever wanted to have persistent storage for your Heroku app because ephemeral storage was just not cutting it?

Cloudcube is a storage addon that gives you an S3 bucket with a 5mb limit in the free tier. Fairly generous! This is great because by default Heroku apps are ephemeral (storage is deleted when a new version is deployed).

Heroku

Install the add on for your Heroku app, or if you're using Review apps then you'll want to add "cloudcube:free" to your addons key in app.json:

{
  "addons": [
    "cloudcube:free"
  ],
}

Django/Python

Make sure you install django-storages and boto3:

$ pip install django-storages boto3
# settings/base.py
import os

# =============================================================================
# Cloudcube storage
# =============================================================================
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

# example cloudcube url: https://cloud-cube.s3.amazonaws.com/bucketname
cloudcube_url = os.environ.get('CLOUDCUBE_URL')
cloudcube_bucket = os.path.basename(cloudcube_url)   # "bucketname"
cloudcube_base_url = os.path.dirname(cloudcube_url)  # "https://cloud-cube.s3.amazonaws.com/" 

AWS_S3_ENDPOINT_URL = cloudcube_base_url
AWS_ACCESS_KEY_ID = os.environ.get('CLOUDCUBE_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.environ.get('CLOUDCUBE_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = cloudcube_bucket
AWS_DEFAULT_ACL = os.environ.get('AWS_DEFAULT_ACL', 'private')
AWS_QUERYSTRING_AUTH = False
AWS_S3_SIGNATURE_VERSION = "s3v4"

We want everything by default to be private, so we've set the AWS_DEFAULT_ACL setting to "private". If we want to override this private setting, we can subclass S3Boto3Storage and change the setting.

For example, static file storage or user image uploads should be public. We'll want to use a "public-read" setting.

Example demonstrating public and private styles:

# custom settings class in yourapp/storages.py
from django.core.files.storage import get_storage_class


class PublicStorage(get_storage_class()):
    default_acl = 'public-read'
    

class PrivateStorage(Storage):
    """When you call file.url using this storage, URLs are automatically appended with
    SAS (Signed Access Signature) parameters."""
    default_acl = 'private'
    secure_urls = True
    querystring_auth = True


# static files in settings/base.py
STATICFILES_STORAGE = 'yourapp.storages.PublicStorage'


# image uploads in someapp/models.py
from yourapp.storages import PublicStorage
class MyModel(models.Model):
    logo = models.ImageField(upload_to='logos', storage=PublicStorage())
    private_upload = models.FileField(upload_to='private', storage=PrivateStorage())

CloudCube interface

Conclusion

Thanks CloudCube folks for giving us free persistent storage! 5mb aint much, but it allows us to give our clients an easy place to upload small CSVs at least. If we need to test more, $5/mo for 5gb aint bad!

..although, it would be neat to have a $1/mo 100mb tier :)


Have any questions?
We'd love to hear from you!